diff --git a/VERSION b/VERSION index 23f3620..f454b81 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.39 +2.44 diff --git a/build b/build index b42a25a..7dc074c 100755 --- a/build +++ b/build @@ -161,6 +161,14 @@ fi check_make_ok # echo +# echo "wiringPi Daemon" +# cd ../wiringPiD +# make -j5 +# check_make_ok +# $sudo make install +# check_make_ok + +# echo # echo "Examples" # cd ../examples # make diff --git a/debian-template/wiringPi/DEBIAN/control b/debian-template/wiringPi/DEBIAN/control index 80cd3f8..ad7d0e1 100644 --- a/debian-template/wiringPi/DEBIAN/control +++ b/debian-template/wiringPi/DEBIAN/control @@ -1,5 +1,5 @@ Package: wiringpi -Version: 2.38 +Version: 2.44 Section: libraries Priority: optional Architecture: armhf diff --git a/examples/Makefile b/examples/Makefile index d6195b3..6d87885 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -33,7 +33,7 @@ INCLUDE = -I/usr/local/include CFLAGS = $(DEBUG) -Wall $(INCLUDE) -Winline -pipe LDFLAGS = -L/usr/local/lib -LDLIBS = -lwiringPi -lwiringPiDev -lpthread -lm +LDLIBS = -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt # Should not alter anything below this line ############################################################################### diff --git a/examples/blink8-drcn.c b/examples/blink8-drcn.c new file mode 100644 index 0000000..96c775b --- /dev/null +++ b/examples/blink8-drcn.c @@ -0,0 +1,61 @@ +/* + * blink8-drcn.c: + * Simple sequence over the first 8 GPIO pins - LEDs + * Aimed at the Ladder board, but it's fairly generic. + * + * Copyright (c) 2012-2013 Gordon Henderson. + *********************************************************************** + * This file is part of wiringPi: + * https://projects.drogon.net/raspberry-pi/wiringpi/ + * + * wiringPi is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * wiringPi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with wiringPi. If not, see . + *********************************************************************** + */ + +#include +#include +#include + +int main (void) +{ + int i, led ; + + printf ("Raspberry Pi - 8-LED Sequencer\n") ; + printf ("==============================\n") ; + printf ("\n") ; + printf ("Connect LEDs to the first 8 GPIO pins and watch ...\n") ; + + int pinBase = 100 ; + +// wiringPiSetup () ; + drcSetupNet (pinBase, 100, "192.168.254.21", "6124", "123456") ; + + for (i = 0 ; i < 8 ; ++i) + pinMode (i + pinBase, OUTPUT) ; + + for (;;) + { + for (led = 0 ; led < 8 ; ++led) + { + digitalWrite (led + pinBase, 1) ; + delay (10) ; + } + + for (led = 0 ; led < 8 ; ++led) + { + digitalWrite (led + pinBase, 0) ; + delay (10) ; + } + } +} diff --git a/gpio/Makefile b/gpio/Makefile index 9152ea8..f41a005 100644 --- a/gpio/Makefile +++ b/gpio/Makefile @@ -37,7 +37,7 @@ INCLUDE = -I$(DESTDIR)$(PREFIX)/include CFLAGS = $(DEBUG) -Wall -Wextra $(INCLUDE) -Winline -pipe LDFLAGS = -L$(DESTDIR)$(PREFIX)/lib -LIBS = -lwiringPi -lwiringPiDev -lpthread -lrt -lm +LIBS = -lwiringPi -lwiringPiDev -lpthread -lrt -lm -lcrypt # May not need to alter anything below this line ############################################################################### diff --git a/gpio/gpio.1 b/gpio/gpio.1 index e5fe181..e5490d6 100644 --- a/gpio/gpio.1 +++ b/gpio/gpio.1 @@ -9,15 +9,15 @@ gpio \- Command-line access to Raspberry Pi's GPIO .PP .B gpio .B [ \-g | \-1 ] -.B mode/read/write/aread/awrite/wb/pwm/clock ... +.B mode/read/write/aread/awrite/wb/pwm/clock/toggle/blink ... .PP .B gpio .B [ \-x extension:params ] -.B mode/read/write/aread/awrite/pwm/pwmTone ... +.B mode/read/write/aread/awrite/pwm/toggle/blink ... .PP .B gpio .B [ \-p ] -.B read/write/toggle/wb +.B read/write/toggle/blink .B ... .PP .B gpio @@ -119,10 +119,22 @@ Write the given value (0 or 1) to the pin. You need to set the pin to output mode first. .TP +.B toggle +Changes the state of a GPIO pin; 0 to 1, or 1 to 0. + +Note unlike the blink command, the pin must be in output mode first. + +.TP +.B blink +Blinks the given pin on/off. Press Control-C to exit. + +Note: This command explicitly sets the pin to output mode. + +.TP .B aread -Read the analog value of the given pin. This needs to be uses in +Read the analog value of the given pin. This needs to be used in conjunction with a -x flag to add in an extension that handles analog -inputs. respective logic levels. +inputs. e.g. gpio -x mcp3002:200:0 aread 200 @@ -132,7 +144,7 @@ will read the first analog input on an mcp3002 SPI ADC chip. .B awrite Write the analog value to the given pin. This needs to be used in conjunction with a -x flag to add in an extension that handles analog -inputs. respective logic levels. +inputs. e.g. gpio -x mcp4802:200:0 awrite 200 128 @@ -234,7 +246,7 @@ absolutely sure you know what you're doing. high | low Change the USB current limiter to high (1.2 amps) or low (the default, 600mA) -This is only applicable to the model B+ +This is only applicable to the Model B+ and the Model B, v2. .TP .B pwm-bal/pwm-ms @@ -253,7 +265,6 @@ them. Optionally it will set the I2C baudrate to that supplied in Kb/sec Note: On recent kernels with the device tree enabled you should use the raspi-config program to load/unload the I2C device at boot time. -(or disable the device tree to continue to use this method) .TP .B load spi @@ -268,7 +279,6 @@ e.g. 8192 bytes then reboot. Note: On recent kernels with the device tree enabled you should use the raspi-config program to load/unload the SPI device at boot time. -(or disable the device tree to continue to use this method) .TP .B gbr diff --git a/gpio/gpio.c b/gpio/gpio.c index 3327308..6fd71f8 100644 --- a/gpio/gpio.c +++ b/gpio/gpio.c @@ -1443,6 +1443,16 @@ int main (int argc, char *argv []) wpMode = WPI_MODE_PIFACE ; } +// Check for -z argument so we don't actually initialise wiringPi + + else if (strcasecmp (argv [1], "-z") == 0) + { + for (i = 2 ; i < argc ; ++i) + argv [i - 1] = argv [i] ; + --argc ; + wpMode = WPI_MODE_UNINITIALISED ; + } + // Default to wiringPi mode else @@ -1460,12 +1470,15 @@ int main (int argc, char *argv []) { if (argc < 3) { - fprintf (stderr, "%s: -x missing extension specification.\n", argv [0]) ; + fprintf (stderr, "%s: -x missing extension command.\n", argv [0]) ; exit (EXIT_FAILURE) ; } - if (!loadWPiExtension (argv [0], argv [2], TRUE)) // Prints its own error messages + if (!loadWPiExtension (argv [0], argv [2], TRUE)) + { + fprintf (stderr, "%s: Extension load failed: %s\n", argv [0], strerror (errno)) ; exit (EXIT_FAILURE) ; + } // Shift args down by 2 diff --git a/gpio/readall.c b/gpio/readall.c index aec6de6..18f836f 100644 --- a/gpio/readall.c +++ b/gpio/readall.c @@ -299,6 +299,8 @@ static void plus2header (int model) printf (" +-----+-----+---------+------+---+--B Plus--+---+------+---------+-----+-----+\n") ; else if (model == PI_MODEL_ZERO) printf (" +-----+-----+---------+------+---+-Pi Zero--+---+------+---------+-----+-----+\n") ; + else if (model == PI_MODEL_ZERO_W) + printf (" +-----+-----+---------+------+---+-Pi ZeroW-+---+------+---------+-----+-----+\n") ; else if (model == PI_MODEL_2) printf (" +-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+\n") ; else if (model == PI_MODEL_3) @@ -346,7 +348,7 @@ void doReadall (void) /**/ if ((model == PI_MODEL_A) || (model == PI_MODEL_B)) abReadall (model, rev) ; - else if ((model == PI_MODEL_BP) || (model == PI_MODEL_AP) || (model == PI_MODEL_2) || (model == PI_MODEL_3) || (model == PI_MODEL_ZERO)) + else if ((model == PI_MODEL_BP) || (model == PI_MODEL_AP) || (model == PI_MODEL_2) || (model == PI_MODEL_3) || (model == PI_MODEL_ZERO) || (model == PI_MODEL_ZERO_W)) piPlusReadall (model) ; else if ((model == PI_MODEL_CM) || (model == PI_MODEL_CM3)) allReadall () ; diff --git a/version.h b/version.h index fde08f3..45b2c50 100644 --- a/version.h +++ b/version.h @@ -1,3 +1,3 @@ -#define VERSION "2.38" +#define VERSION "2.44" #define VERSION_MAJOR 2 -#define VERSION_MINOR 38 +#define VERSION_MINOR 44 diff --git a/wiringPi/Makefile b/wiringPi/Makefile index 13d006f..e1868b9 100644 --- a/wiringPi/Makefile +++ b/wiringPi/Makefile @@ -41,7 +41,7 @@ INCLUDE = -I. DEFS = -D_GNU_SOURCE CFLAGS = $(DEBUG) $(DEFS) -Wformat=2 -Wall -Wextra -Winline $(INCLUDE) -pipe -fPIC -LIBS = -lm -lpthread -lrt +LIBS = -lm -lpthread -lrt -lcrypt ############################################################################### @@ -57,8 +57,8 @@ SRC = wiringPi.c \ mcp3002.c mcp3004.c mcp4802.c mcp3422.c \ max31855.c max5322.c ads1115.c \ sn3218.c \ - bmp180.c htu21d.c ds18b20.c \ - drcSerial.c \ + bmp180.c htu21d.c ds18b20.c rht03.c \ + drcSerial.c drcNet.c \ pseudoPins.c \ wpiExtensions.c diff --git a/wiringPi/drcNet.c b/wiringPi/drcNet.c new file mode 100644 index 0000000..0964ff7 --- /dev/null +++ b/wiringPi/drcNet.c @@ -0,0 +1,405 @@ +/* + * drcNet.h: + * Extend wiringPi with the DRC Network protocol (e.g. to another Pi) + * Copyright (c) 2016-2017 Gordon Henderson + *********************************************************************** + * This file is part of wiringPi: + * https://projects.drogon.net/raspberry-pi/wiringpi/ + * + * wiringPi is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * wiringPi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with wiringPi. + * If not, see . + *********************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "wiringPi.h" +#include "drcNet.h" +#include "../wiringPiD/drcNetCmd.h" + + +/* + * remoteReadline: + * Read in a line of data from the remote server, ending with a newline + * character which is not stored. Returns the length or < 0 on + * any sort of failure. + ********************************************************************************* + */ + +static int remoteReadline (int fd, char *buf, int max) +{ + int len = 0 ; + char c ; + + for (;;) + { + if (read (fd, &c, 1) < 1) + return -1 ; + + if (c == '\n') + return len ; + + *buf++ = c ; + if (++len == max) + return len ; + } +} + + +/* + * getChallenge: + * Read in lines from the remote site until we get one identified + * as the challenge. This line contains the password salt. + ********************************************************************************* + */ + +static char *getChallenge (int fd) +{ + static char buf [1024] ; + int num ; + + for (;;) + { + if ((num = remoteReadline (fd, buf, 1023)) < 0) + return NULL ; + buf [num] = 0 ; + + if (strncmp (buf, "Challenge ", 10) == 0) + return &buf [10] ; + } +} + + +/* + * authenticate: + * Read in the challenge from the server, use it to encrypt our password + * and send it back to the server. Wait for a reply back from the server + * to say that we're good to go. + * The server will simply disconnect on a bad response. No 3 chances here. + ********************************************************************************* + */ + +static int authenticate (int fd, const char *pass) +{ + char *challenge ; + char *encrypted ; + char salted [1024] ; + + if ((challenge = getChallenge (fd)) == NULL) + return -1 ; + + sprintf (salted, "$6$%s$", challenge) ; + encrypted = crypt (pass, salted) ; + +// This is an assertion, or sanity check on my part... +// The '20' comes from the $6$ then the 16 characters of the salt, +// then the terminating $. + + if (strncmp (encrypted, salted, 20) != 0) + { + errno = EBADE ; + return -1 ; + } + +// 86 characters is the length of the SHA-256 hash + + if (write (fd, encrypted + 20, 86) == 86) + return 0 ; + else + return -1 ; +} + + +/* + * _drcSetupNet: + * Do the hard work of establishing a network connection and authenticating + * the password. + ********************************************************************************* + */ + +int _drcSetupNet (const char *ipAddress, const char *port, const char *password) +{ + struct addrinfo hints; + struct addrinfo *result, *rp ; + struct in6_addr serveraddr ; + int remoteFd ; + +// Start by seeing if we've been given a (textual) numeric IP address +// which will save lookups in getaddrinfo() + + memset (&hints, 0, sizeof (hints)) ; + hints.ai_flags = AI_NUMERICSERV ; + hints.ai_family = AF_UNSPEC ; + hints.ai_socktype = SOCK_STREAM ; + hints.ai_protocol = 0 ; + + if (inet_pton (AF_INET, ipAddress, &serveraddr) == 1) // Valid IPv4 + { + hints.ai_family = AF_INET ; + hints.ai_flags |= AI_NUMERICHOST ; + } + else + { + if (inet_pton (AF_INET6, ipAddress, &serveraddr) == 1) // Valid IPv6 + { + hints.ai_family = AF_INET6 ; + hints.ai_flags |= AI_NUMERICHOST ; + } + } + +// Now use getaddrinfo() with the newly supplied hints + + if (getaddrinfo (ipAddress, port, &hints, &result) != 0) + return -1 ; + +// Now try each address in-turn until we get one that connects... + + for (rp = result; rp != NULL; rp = rp->ai_next) + { + if ((remoteFd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0) + continue ; + + if (connect (remoteFd, rp->ai_addr, rp->ai_addrlen) < 0) + continue ; + + if (authenticate (remoteFd, password) < 0) + { + close (remoteFd) ; + errno = EACCES ; // Permission denied + return -1 ; + } + else + return remoteFd ; + } + + errno = EHOSTUNREACH ; // Host unreachable - may not be right, but good enough + return -1 ; // Nothing connected +} + + +/* + * myPinMode: + * Change the pin mode on the remote DRC device + ********************************************************************************* + */ + +static void myPinMode (struct wiringPiNodeStruct *node, int pin, int mode) +{ + struct drcNetComStruct cmd ; + + cmd.pin = pin - node->pinBase ; + cmd.cmd = DRCN_PIN_MODE ; + cmd.data = mode ; + + (void)send (node->fd, &cmd, sizeof (cmd), 0) ; + (void)recv (node->fd, &cmd, sizeof (cmd), 0) ; +} + + +/* + * myPullUpDnControl: + ********************************************************************************* + */ + +static void myPullUpDnControl (struct wiringPiNodeStruct *node, int pin, int mode) +{ + struct drcNetComStruct cmd ; + + cmd.pin = pin - node->pinBase ; + cmd.cmd = DRCN_PULL_UP_DN ; + cmd.data = mode ; + + (void)send (node->fd, &cmd, sizeof (cmd), 0) ; + (void)recv (node->fd, &cmd, sizeof (cmd), 0) ; +} + + +/* + * myDigitalWrite: + ********************************************************************************* + */ + +static void myDigitalWrite (struct wiringPiNodeStruct *node, int pin, int value) +{ + struct drcNetComStruct cmd ; + + cmd.pin = pin - node->pinBase ; + cmd.cmd = DRCN_DIGITAL_WRITE ; + cmd.data = value ; + + (void)send (node->fd, &cmd, sizeof (cmd), 0) ; + (void)recv (node->fd, &cmd, sizeof (cmd), 0) ; +} + + +/* + * myDigitalWrite8: + ********************************************************************************* + +static void myDigitalWrite8 (struct wiringPiNodeStruct *node, int pin, int value) +{ + struct drcNetComStruct cmd ; + + cmd.pin = pin - node->pinBase ; + cmd.cmd = DRCN_DIGITAL_WRITE8 ; + cmd.data = value ; + + (void)send (node->fd, &cmd, sizeof (cmd), 0) ; + (void)recv (node->fd, &cmd, sizeof (cmd), 0) ; +} + */ + + +/* + * myAnalogWrite: + ********************************************************************************* + */ + +static void myAnalogWrite (struct wiringPiNodeStruct *node, int pin, int value) +{ + struct drcNetComStruct cmd ; + + cmd.pin = pin - node->pinBase ; + cmd.cmd = DRCN_ANALOG_WRITE ; + cmd.data = value ; + + (void)send (node->fd, &cmd, sizeof (cmd), 0) ; + (void)recv (node->fd, &cmd, sizeof (cmd), 0) ; +} + + +/* + * myPwmWrite: + ********************************************************************************* + */ + +static void myPwmWrite (struct wiringPiNodeStruct *node, int pin, int value) +{ + struct drcNetComStruct cmd ; + + cmd.pin = pin - node->pinBase ; + cmd.cmd = DRCN_PWM_WRITE ; + cmd.data = value ; + + (void)send (node->fd, &cmd, sizeof (cmd), 0) ; + (void)recv (node->fd, &cmd, sizeof (cmd), 0) ; +} + + +/* + * myAnalogRead: + ********************************************************************************* + */ + +static int myAnalogRead (struct wiringPiNodeStruct *node, int pin) +{ + struct drcNetComStruct cmd ; + + cmd.pin = pin - node->pinBase ; + cmd.cmd = DRCN_ANALOG_READ ; + cmd.data = 0 ; + + (void)send (node->fd, &cmd, sizeof (cmd), 0) ; + (void)recv (node->fd, &cmd, sizeof (cmd), 0) ; + + return cmd.data ; +} + + +/* + * myDigitalRead: + ********************************************************************************* + */ + +static int myDigitalRead (struct wiringPiNodeStruct *node, int pin) +{ + struct drcNetComStruct cmd ; + + cmd.pin = pin - node->pinBase ; + cmd.cmd = DRCN_DIGITAL_READ ; + cmd.data = 0 ; + + (void)send (node->fd, &cmd, sizeof (cmd), 0) ; + (void)recv (node->fd, &cmd, sizeof (cmd), 0) ; + + return cmd.data ; +} + + +/* + * myDigitalRead8: + ********************************************************************************* + +static unsigned int myDigitalRead8 (struct wiringPiNodeStruct *node, int pin) +{ + struct drcNetComStruct cmd ; + + cmd.pin = pin - node->pinBase ; + cmd.cmd = DRCN_DIGITAL_READ8 ; + cmd.data = 0 ; + + (void)send (node->fd, &cmd, sizeof (cmd), 0) ; + (void)recv (node->fd, &cmd, sizeof (cmd), 0) ; + + return cmd.data ; +} + */ + + +/* + * drcNet: + * Create a new instance of an DRC GPIO interface. + * Could be a variable nunber of pins here - we might not know in advance. + ********************************************************************************* + */ + +int drcSetupNet (const int pinBase, const int numPins, const char *ipAddress, const char *port, const char *password) +{ + int fd, len ; + struct wiringPiNodeStruct *node ; + + if ((fd = _drcSetupNet (ipAddress, port, password)) < 0) + return FALSE ; + + len = sizeof (struct drcNetComStruct) ; + + if (setsockopt (fd, SOL_SOCKET, SO_RCVLOWAT, (void *)&len, sizeof (len)) < 0) + return FALSE ; + + node = wiringPiNewNode (pinBase, numPins) ; + + node->fd = fd ; + node->pinMode = myPinMode ; + node->pullUpDnControl = myPullUpDnControl ; + node->analogRead = myAnalogRead ; + node->analogRead = myAnalogRead ; + node->analogWrite = myAnalogWrite ; + node->digitalRead = myDigitalRead ; + node->digitalWrite = myDigitalWrite ; +//node->digitalRead8 = myDigitalRead8 ; +//node->digitalWrite8 = myDigitalWrite8 ; + node->pwmWrite = myPwmWrite ; + + return TRUE ; +} diff --git a/wiringPi/drcNet.h b/wiringPi/drcNet.h new file mode 100644 index 0000000..00f9b05 --- /dev/null +++ b/wiringPi/drcNet.h @@ -0,0 +1,42 @@ +/* + * drcNet.h: + * Extend wiringPi with the DRC Network protocol (e.g. to another Pi) + * Copyright (c) 2016-2017 Gordon Henderson + *********************************************************************** + * This file is part of wiringPi: + * https://projects.drogon.net/raspberry-pi/wiringpi/ + * + * wiringPi is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * wiringPi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with wiringPi. + * If not, see . + *********************************************************************** + */ + +/********* +struct drcNetStruct +{ + uint32_t pin ; + uint32_t cmd ; + uint32_t data ; +} ; +**************/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern int drcSetupNet (const int pinBase, const int numPins, const char *ipAddress, const char *port, const char *password) ; + +#ifdef __cplusplus +} +#endif diff --git a/wiringPi/rht03.c b/wiringPi/rht03.c new file mode 100644 index 0000000..1129cfd --- /dev/null +++ b/wiringPi/rht03.c @@ -0,0 +1,252 @@ +/* + * rht03.c: + * Extend wiringPi with the rht03 Maxdetect 1-Wire sensor. + * Copyright (c) 2016-2017 Gordon Henderson + *********************************************************************** + * This file is part of wiringPi: + * https://projects.drogon.net/raspberry-pi/wiringpi/ + * + * wiringPi is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * wiringPi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with wiringPi. + * If not, see . + *********************************************************************** + */ + +#include +#include +#include +#include + +#include "wiringPi.h" +#include "rht03.h" + +/* + * maxDetectLowHighWait: + * Wait for a transition from low to high on the bus + ********************************************************************************* + */ + +static int maxDetectLowHighWait (const int pin) +{ + struct timeval now, timeOut, timeUp ; + +// If already high then wait for pin to go low + + gettimeofday (&now, NULL) ; + timerclear (&timeOut) ; + timeOut.tv_usec = 1000 ; + timeradd (&now, &timeOut, &timeUp) ; + + while (digitalRead (pin) == HIGH) + { + gettimeofday (&now, NULL) ; + if (timercmp (&now, &timeUp, >)) + return FALSE ; + } + +// Wait for it to go HIGH + + gettimeofday (&now, NULL) ; + timerclear (&timeOut) ; + timeOut.tv_usec = 1000 ; + timeradd (&now, &timeOut, &timeUp) ; + + while (digitalRead (pin) == LOW) + { + gettimeofday (&now, NULL) ; + if (timercmp (&now, &timeUp, >)) + return FALSE ; + } + + return TRUE ; +} + + +/* + * maxDetectClockByte: + * Read in a single byte from the MaxDetect bus + ********************************************************************************* + */ + +static unsigned int maxDetectClockByte (const int pin) +{ + unsigned int byte = 0 ; + int bit ; + + for (bit = 0 ; bit < 8 ; ++bit) + { + if (!maxDetectLowHighWait (pin)) + return 0 ; + +// bit starting now - we need to time it. + + delayMicroseconds (30) ; + byte <<= 1 ; + if (digitalRead (pin) == HIGH) // It's a 1 + byte |= 1 ; + } + + return byte ; +} + + +/* + * maxDetectRead: + * Read in and return the 4 data bytes from the MaxDetect sensor. + * Return TRUE/FALSE depending on the checksum validity + ********************************************************************************* + */ + +static int maxDetectRead (const int pin, unsigned char buffer [4]) +{ + int i ; + unsigned int checksum ; + unsigned char localBuf [5] ; + struct timeval now, then, took ; + +// See how long we took + + gettimeofday (&then, NULL) ; + +// Wake up the RHT03 by pulling the data line low, then high +// Low for 10mS, high for 40uS. + + pinMode (pin, OUTPUT) ; + digitalWrite (pin, 0) ; delay (10) ; + digitalWrite (pin, 1) ; delayMicroseconds (40) ; + pinMode (pin, INPUT) ; + +// Now wait for sensor to pull pin low + + if (!maxDetectLowHighWait (pin)) + return FALSE ; + +// and read in 5 bytes (40 bits) + + for (i = 0 ; i < 5 ; ++i) + localBuf [i] = maxDetectClockByte (pin) ; + + checksum = 0 ; + for (i = 0 ; i < 4 ; ++i) + { + buffer [i] = localBuf [i] ; + checksum += localBuf [i] ; + } + checksum &= 0xFF ; + +// See how long we took + + gettimeofday (&now, NULL) ; + timersub (&now, &then, &took) ; + +// Total time to do this should be: +// 10mS + 40µS - reset +// + 80µS + 80µS - sensor doing its low -> high thing +// + 40 * (50µS + 27µS (0) or 70µS (1) ) +// = 15010µS +// so if we take more than that, we've had a scheduling interruption and the +// reading is probably bogus. + + if ((took.tv_sec != 0) || (took.tv_usec > 16000)) + return FALSE ; + + return checksum == localBuf [4] ; +} + + +/* + * myReadRHT03: + * Read the Temperature & Humidity from an RHT03 sensor + * Values returned are *10, so 123 is 12.3. + ********************************************************************************* + */ + +static int myReadRHT03 (const int pin, int *temp, int *rh) +{ + int result ; + unsigned char buffer [4] ; + +// Read ... + + result = maxDetectRead (pin, buffer) ; + + if (!result) + return FALSE ; + + *rh = (buffer [0] * 256 + buffer [1]) ; + *temp = (buffer [2] * 256 + buffer [3]) ; + + if ((*temp & 0x8000) != 0) // Negative + { + *temp &= 0x7FFF ; + *temp = -*temp ; + } + +// Discard obviously bogus readings - the checksum can't detect a 2-bit error +// (which does seem to happen - no realtime here) + + if ((*rh > 999) || (*temp > 800) || (*temp < -400)) + return FALSE ; + + return TRUE ; +} + + +/* + * myAnalogRead: + ********************************************************************************* + */ + +static int myAnalogRead (struct wiringPiNodeStruct *node, int pin) +{ + int piPin = node->fd ; + int chan = pin - node->pinBase ; + int temp = -9997 ; + int rh = -9997 ; + int try ; + + if (chan > 1) + return -9999 ; // Bad parameters + + for (try = 0 ; try < 10 ; ++try) + { + if (myReadRHT03 (piPin, &temp, &rh)) + return chan == 0 ? temp : rh ; + } + + return -9998 ; +} + + +/* + * rht03Setup: + * Create a new instance of an RHT03 temperature sensor. + ********************************************************************************* + */ + +int rht03Setup (const int pinBase, const int piPin) +{ + struct wiringPiNodeStruct *node ; + + if ((piPin & PI_GPIO_MASK) != 0) // Must be an on-board pin + return FALSE ; + +// 2 pins - temperature and humidity + + node = wiringPiNewNode (pinBase, 2) ; + + node->fd = piPin ; + node->analogRead = myAnalogRead ; + + return TRUE ; +} diff --git a/wiringPi/rht03.h b/wiringPi/rht03.h new file mode 100644 index 0000000..9523fbf --- /dev/null +++ b/wiringPi/rht03.h @@ -0,0 +1,25 @@ +/* + * rht03.h: + * Extend wiringPi with the rht03 Maxdetect 1-Wire sensor. + * Copyright (c) 2016-2017 Gordon Henderson + *********************************************************************** + * This file is part of wiringPi: + * https://projects.drogon.net/raspberry-pi/wiringpi/ + * + * wiringPi is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * wiringPi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with wiringPi. + * If not, see . + *********************************************************************** + */ + +extern int rht03Setup (const int pinBase, const int devicePin) ; diff --git a/wiringPi/softPwm.c b/wiringPi/softPwm.c index 5999cc0..d99fa00 100644 --- a/wiringPi/softPwm.c +++ b/wiringPi/softPwm.c @@ -1,7 +1,7 @@ /* * softPwm.c: - * Provide 2 channels of software driven PWM. - * Copyright (c) 2012-2014 Gordon Henderson + * Provide many channels of software driven PWM. + * Copyright (c) 2012-2017 Gordon Henderson *********************************************************************** * This file is part of wiringPi: * https://projects.drogon.net/raspberry-pi/wiringpi/ @@ -30,11 +30,11 @@ #include "softPwm.h" // MAX_PINS: -// This is more than the number of Pi pins because we can actually softPwm -// pins that are on GPIO expanders. It's not that efficient and more than 1 or -// 2 pins on e.g. (SPI) mcp23s17 won't really be that effective, however... +// This is more than the number of Pi pins because we can actually softPwm. +// Once upon a time I let pins on gpio expanders be softPwm'd, but it's really +// really not a good thing. -#define MAX_PINS 1024 +#define MAX_PINS 64 // The PWM Frequency is derived from the "pulse time" below. Essentially, // the frequency is a function of the range and this pulse time. @@ -45,7 +45,7 @@ // It's possible to get a higher frequency by lowering the pulse time, // however CPU uage will skyrocket as wiringPi uses a hard-loop to time // periods under 100µS - this is because the Linux timer calls are just -// accurate at all, and have an overhead. +// not accurate at all, and have an overhead. // // Another way to increase the frequency is to reduce the range - however // that reduces the overall output accuracy... @@ -106,14 +106,15 @@ static void *softPwmThread (void *arg) void softPwmWrite (int pin, int value) { - pin &= (MAX_PINS - 1) ; - - /**/ if (value < 0) - value = 0 ; - else if (value > range [pin]) - value = range [pin] ; + if (pin < MAX_PINS) + { + /**/ if (value < 0) + value = 0 ; + else if (value > range [pin]) + value = range [pin] ; - marks [pin] = value ; + marks [pin] = value ; + } } @@ -129,6 +130,9 @@ int softPwmCreate (int pin, int initialValue, int pwmRange) pthread_t myThread ; int *passPin ; + if (pin >= MAX_PINS) + return -1 ; + if (range [pin] != 0) // Already running on this pin return -1 ; @@ -139,15 +143,15 @@ int softPwmCreate (int pin, int initialValue, int pwmRange) if (passPin == NULL) return -1 ; - pinMode (pin, OUTPUT) ; digitalWrite (pin, LOW) ; + pinMode (pin, OUTPUT) ; marks [pin] = initialValue ; range [pin] = pwmRange ; *passPin = pin ; - newPin = pin ; - res = pthread_create (&myThread, NULL, softPwmThread, (void *)passPin) ; + newPin = pin ; + res = pthread_create (&myThread, NULL, softPwmThread, (void *)passPin) ; while (newPin != -1) delay (1) ; @@ -166,11 +170,14 @@ int softPwmCreate (int pin, int initialValue, int pwmRange) void softPwmStop (int pin) { - if (range [pin] != 0) + if (pin < MAX_PINS) { - pthread_cancel (threads [pin]) ; - pthread_join (threads [pin], NULL) ; - range [pin] = 0 ; - digitalWrite (pin, LOW) ; + if (range [pin] != 0) + { + pthread_cancel (threads [pin]) ; + pthread_join (threads [pin], NULL) ; + range [pin] = 0 ; + digitalWrite (pin, LOW) ; + } } } diff --git a/wiringPi/wiringPi.c b/wiringPi/wiringPi.c index ba86f41..bd1a369 100644 --- a/wiringPi/wiringPi.c +++ b/wiringPi/wiringPi.c @@ -84,10 +84,8 @@ #define ENV_GPIOMEM "WIRINGPI_GPIOMEM" -// Mask for the bottom 64 pins which belong to the Raspberry Pi -// The others are available for the other devices - -#define PI_GPIO_MASK (0xFFFFFFC0) +// Extend wiringPi with other pin-based devices and keep track of +// them in this structure struct wiringPiNodeStruct *wiringPiNodes = NULL ; @@ -224,7 +222,7 @@ const char *piModelNames [16] = "Pi Zero", // 09 "CM3", // 10 "Unknown11", // 11 - "Unknown12", // 12 + "Pi Zero-W", // 12 "Unknown13", // 13 "Unknown14", // 14 "Unknown15", // 15 @@ -1244,13 +1242,15 @@ struct wiringPiNodeStruct *wiringPiFindNode (int pin) ********************************************************************************* */ -static void pinModeDummy (UNU struct wiringPiNodeStruct *node, UNU int pin, UNU int mode) { return ; } -static void pullUpDnControlDummy (UNU struct wiringPiNodeStruct *node, UNU int pin, UNU int pud) { return ; } -static int digitalReadDummy (UNU struct wiringPiNodeStruct *node, UNU int UNU pin) { return LOW ; } -static void digitalWriteDummy (UNU struct wiringPiNodeStruct *node, UNU int pin, UNU int value) { return ; } -static void pwmWriteDummy (UNU struct wiringPiNodeStruct *node, UNU int pin, UNU int value) { return ; } -static int analogReadDummy (UNU struct wiringPiNodeStruct *node, UNU int pin) { return 0 ; } -static void analogWriteDummy (UNU struct wiringPiNodeStruct *node, UNU int pin, UNU int value) { return ; } +static void pinModeDummy (UNU struct wiringPiNodeStruct *node, UNU int pin, UNU int mode) { return ; } +static void pullUpDnControlDummy (UNU struct wiringPiNodeStruct *node, UNU int pin, UNU int pud) { return ; } +static unsigned int digitalRead8Dummy (UNU struct wiringPiNodeStruct *node, UNU int UNU pin) { return 0 ; } +static void digitalWrite8Dummy (UNU struct wiringPiNodeStruct *node, UNU int pin, UNU int value) { return ; } +static int digitalReadDummy (UNU struct wiringPiNodeStruct *node, UNU int UNU pin) { return LOW ; } +static void digitalWriteDummy (UNU struct wiringPiNodeStruct *node, UNU int pin, UNU int value) { return ; } +static void pwmWriteDummy (UNU struct wiringPiNodeStruct *node, UNU int pin, UNU int value) { return ; } +static int analogReadDummy (UNU struct wiringPiNodeStruct *node, UNU int pin) { return 0 ; } +static void analogWriteDummy (UNU struct wiringPiNodeStruct *node, UNU int pin, UNU int value) { return ; } struct wiringPiNodeStruct *wiringPiNewNode (int pinBase, int numPins) { @@ -1272,17 +1272,19 @@ struct wiringPiNodeStruct *wiringPiNewNode (int pinBase, int numPins) if (node == NULL) (void)wiringPiFailure (WPI_FATAL, "wiringPiNewNode: Unable to allocate memory: %s\n", strerror (errno)) ; - node->pinBase = pinBase ; - node->pinMax = pinBase + numPins - 1 ; - node->pinMode = pinModeDummy ; - node->pullUpDnControl = pullUpDnControlDummy ; - node->digitalRead = digitalReadDummy ; - node->digitalWrite = digitalWriteDummy ; - node->pwmWrite = pwmWriteDummy ; - node->analogRead = analogReadDummy ; - node->analogWrite = analogWriteDummy ; - node->next = wiringPiNodes ; - wiringPiNodes = node ; + node->pinBase = pinBase ; + node->pinMax = pinBase + numPins - 1 ; + node->pinMode = pinModeDummy ; + node->pullUpDnControl = pullUpDnControlDummy ; + node->digitalRead = digitalReadDummy ; +//node->digitalRead8 = digitalRead8Dummy ; + node->digitalWrite = digitalWriteDummy ; +//node->digitalWrite8 = digitalWrite8Dummy ; + node->pwmWrite = pwmWriteDummy ; + node->analogRead = analogReadDummy ; + node->analogWrite = analogWriteDummy ; + node->next = wiringPiNodes ; + wiringPiNodes = node ; return node ; } @@ -1493,6 +1495,27 @@ int digitalRead (int pin) /* + * digitalRead8: + * Read 8-bits (a byte) from given start pin. + ********************************************************************************* + +unsigned int digitalRead8 (int pin) +{ + struct wiringPiNodeStruct *node = wiringPiNodes ; + + if ((pin & PI_GPIO_MASK) == 0) // On-Board Pin + return 0 ; + else + { + if ((node = wiringPiFindNode (pin)) == NULL) + return LOW ; + return node->digitalRead8 (node, pin) ; + } +} + */ + + +/* * digitalWrite: * Set an output bit ********************************************************************************* @@ -1536,6 +1559,26 @@ void digitalWrite (int pin, int value) /* + * digitalWrite8: + * Set an output 8-bit byte on the device from the given pin number + ********************************************************************************* + +void digitalWrite8 (int pin, int value) +{ + struct wiringPiNodeStruct *node = wiringPiNodes ; + + if ((pin & PI_GPIO_MASK) == 0) // On-Board Pin + return ; + else + { + if ((node = wiringPiFindNode (pin)) != NULL) + node->digitalWrite8 (node, pin, value) ; + } +} + */ + + +/* * pwmWrite: * Set an output PWM value ********************************************************************************* @@ -2144,9 +2187,10 @@ int wiringPiSetup (void) switch (model) { - 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_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: piGpioBase = GPIO_PERI_BASE_OLD ; break ; diff --git a/wiringPi/wiringPi.h b/wiringPi/wiringPi.h index 1b50470..f601f13 100644 --- a/wiringPi/wiringPi.h +++ b/wiringPi/wiringPi.h @@ -26,6 +26,7 @@ // C doesn't have true/false by default and I can never remember which // way round they are, so ... +// (and yes, I know about stdbool.h but I like capitals for these and I'm old) #ifndef TRUE # define TRUE (1==1) @@ -36,6 +37,11 @@ #define UNU __attribute__((unused)) +// Mask for the bottom 64 pins which belong to the Raspberry Pi +// The others are available for the other devices + +#define PI_GPIO_MASK (0xFFFFFFC0) + // Handy defines // wiringPi modes @@ -92,6 +98,7 @@ #define PI_MODEL_3 8 #define PI_MODEL_ZERO 9 #define PI_MODEL_CM3 10 +#define PI_MODEL_ZERO_W 12 #define PI_VERSION_1 0 #define PI_VERSION_1_1 1 @@ -140,13 +147,15 @@ struct wiringPiNodeStruct unsigned int data2 ; // ditto unsigned int data3 ; // ditto - void (*pinMode) (struct wiringPiNodeStruct *node, int pin, int mode) ; - void (*pullUpDnControl) (struct wiringPiNodeStruct *node, int pin, int mode) ; - int (*digitalRead) (struct wiringPiNodeStruct *node, int pin) ; - void (*digitalWrite) (struct wiringPiNodeStruct *node, int pin, int value) ; - void (*pwmWrite) (struct wiringPiNodeStruct *node, int pin, int value) ; - int (*analogRead) (struct wiringPiNodeStruct *node, int pin) ; - void (*analogWrite) (struct wiringPiNodeStruct *node, int pin, int value) ; + void (*pinMode) (struct wiringPiNodeStruct *node, int pin, int mode) ; + void (*pullUpDnControl) (struct wiringPiNodeStruct *node, int pin, int mode) ; + int (*digitalRead) (struct wiringPiNodeStruct *node, int pin) ; +//unsigned int (*digitalRead8) (struct wiringPiNodeStruct *node, int pin) ; + void (*digitalWrite) (struct wiringPiNodeStruct *node, int pin, int value) ; +// void (*digitalWrite8) (struct wiringPiNodeStruct *node, int pin, int value) ; + void (*pwmWrite) (struct wiringPiNodeStruct *node, int pin, int value) ; + int (*analogRead) (struct wiringPiNodeStruct *node, int pin) ; + void (*analogWrite) (struct wiringPiNodeStruct *node, int pin, int value) ; struct wiringPiNodeStruct *next ; } ; @@ -179,14 +188,16 @@ extern int wiringPiSetupSys (void) ; extern int wiringPiSetupGpio (void) ; extern int wiringPiSetupPhys (void) ; -extern void pinModeAlt (int pin, int mode) ; -extern void pinMode (int pin, int mode) ; -extern void pullUpDnControl (int pin, int pud) ; -extern int digitalRead (int pin) ; -extern void digitalWrite (int pin, int value) ; -extern void pwmWrite (int pin, int value) ; -extern int analogRead (int pin) ; -extern void analogWrite (int pin, int value) ; +extern void pinModeAlt (int pin, int mode) ; +extern void pinMode (int pin, int mode) ; +extern void pullUpDnControl (int pin, int pud) ; +extern int digitalRead (int pin) ; +extern void digitalWrite (int pin, int value) ; +extern unsigned int digitalRead8 (int pin) ; +extern void digitalWrite8 (int pin, int value) ; +extern void pwmWrite (int pin, int value) ; +extern int analogRead (int pin) ; +extern void analogWrite (int pin, int value) ; // PiFace specifics // (Deprecated) @@ -204,12 +215,14 @@ extern int physPinToGpio (int physPin) ; extern void setPadDrive (int group, int value) ; extern int getAlt (int pin) ; extern void pwmToneWrite (int pin, int freq) ; -extern void digitalWriteByte (int value) ; -extern unsigned int digitalReadByte (void) ; extern void pwmSetMode (int mode) ; extern void pwmSetRange (unsigned int range) ; extern void pwmSetClock (int divisor) ; extern void gpioClockSet (int pin, int freq) ; +extern unsigned int digitalReadByte (void) ; +extern unsigned int digitalReadByte2 (void) ; +extern void digitalWriteByte (int value) ; +extern void digitalWriteByte2 (int value) ; // Interrupts // (Also Pi hardware specific) diff --git a/wiringPi/wpiExtensions.c b/wiringPi/wpiExtensions.c index 2761945..53fafc0 100644 --- a/wiringPi/wpiExtensions.c +++ b/wiringPi/wpiExtensions.c @@ -55,10 +55,13 @@ #include "ads1115.h" #include "sn3218.h" #include "drcSerial.h" +#include "drcNet.h" +#include "../wiringPiD/drcNetCmd.h" #include "pseudoPins.h" #include "bmp180.h" #include "htu21d.h" #include "ds18b20.h" +#include "rht03.h" #include "wpiExtensions.h" @@ -134,12 +137,16 @@ static char *extractInt (char *progName, char *p, int *num) /* * extractStr: * Check & return a string at the given location (prefixed by a :) + * Note: The string can be enclosed in []'s to escape colons. This is + * so we can handle IPv6 addresses which contain colons and the []'s is + * a common way to prepresent them. ********************************************************************************* */ static char *extractStr (char *progName, char *p, char **str) { char *q, *r ; + int quoted = FALSE ; if (*p != ':') { @@ -149,21 +156,38 @@ static char *extractStr (char *progName, char *p, char **str) ++p ; - if (!isprint (*p)) + if (*p == '[') + { + quoted = TRUE ; + ++p ; + } + + if (!isprint (*p)) // Is this needed? { verbError ("%s: character expected", progName) ; return NULL ; } q = p ; - while ((*q != 0) && (*q != ':')) - ++q ; + if (quoted) + { + while ((*q != 0) && (*q != ']')) + ++q ; + } + else + { + while ((*q != 0) && (*q != ':')) + ++q ; + } *str = r = calloc (q - p + 2, 1) ; // Zeros it while (p != q) *r++ = *p++ ; - + + if (quoted) // Skip over the ] to the : + ++p ; + return p ; } @@ -496,6 +520,24 @@ static int doExtensionDs18b20 (char *progName, int pinBase, char *params) /* + * doExtensionRht03: + * Maxdetect 1-Wire Temperature & Humidity + * rht03:base:piPin + ********************************************************************************* + */ + +static int doExtensionRht03 (char *progName, int pinBase, char *params) +{ + int piPin ; + + if ((params = extractInt (progName, params, &piPin)) == NULL) + return FALSE ; + + return rht03Setup (pinBase, piPin) ; +} + + +/* * doExtensionMax31855: * Analog IO * max31855:base:spiChan @@ -698,9 +740,9 @@ static int doExtensionDrcS (char *progName, int pinBase, char *params) if ((params = extractInt (progName, params, &pins)) == NULL) return FALSE ; - if ((pins < 1) || (pins > 100)) + if ((pins < 1) || (pins > 1000)) { - verbError ("%s: pins (%d) out of range (2-100)", progName, pins) ; + verbError ("%s: pins (%d) out of range (2-1000)", progName, pins) ; return FALSE ; } @@ -728,6 +770,59 @@ static int doExtensionDrcS (char *progName, int pinBase, char *params) } +/* + * doExtensionDrcNet: + * Interface to a DRC Network system + * drcn:base:pins:ipAddress:port:password + ********************************************************************************* + */ + +static int doExtensionDrcNet (char *progName, int pinBase, char *params) +{ + int pins ; + char *ipAddress, *port, *password ; + char pPort [1024] ; + + if ((params = extractInt (progName, params, &pins)) == NULL) + return FALSE ; + + if ((pins < 1) || (pins > 1000)) + { + verbError ("%s: pins (%d) out of range (2-1000)", progName, pins) ; + return FALSE ; + } + + if ((params = extractStr (progName, params, &ipAddress)) == NULL) + return FALSE ; + + if (strlen (ipAddress) == 0) + { + verbError ("%s: ipAddress required", progName) ; + return FALSE ; + } + + if ((params = extractStr (progName, params, &port)) == NULL) + return FALSE ; + + if (strlen (port) == 0) + { + sprintf (pPort, "%d", DEFAULT_SERVER_PORT) ; + port = pPort ; + } + + if ((params = extractStr (progName, params, &password)) == NULL) + return FALSE ; + + if (strlen (password) == 0) + { + verbError ("%s: password required", progName) ; + return FALSE ; + } + + return drcSetupNet (pinBase, pins, ipAddress, port, password) ; +} + + /* * Function list @@ -748,6 +843,7 @@ static struct extensionFunctionStruct extensionFunctions [] = { "pseudoPins", &doExtensionPseudoPins }, { "htu21d", &doExtensionHtu21d }, { "ds18b20", &doExtensionDs18b20 }, + { "rht03", &doExtensionRht03 }, { "mcp3002", &doExtensionMcp3002 }, { "mcp3004", &doExtensionMcp3004 }, { "mcp4802", &doExtensionMcp4802 }, @@ -757,6 +853,7 @@ static struct extensionFunctionStruct extensionFunctions [] = { "max5322", &doExtensionMax5322 }, { "sn3218", &doExtensionSn3218 }, { "drcs", &doExtensionDrcS }, + { "drcn", &doExtensionDrcNet }, { NULL, NULL }, } ; @@ -826,6 +923,6 @@ int loadWPiExtension (char *progName, char *extensionData, int printErrors) return extensionFn->function (progName, pinBase, p) ; } - verbError ("%s: extension %s not found", progName, extension) ; + fprintf (stderr, "%s: extension %s not found", progName, extension) ; return FALSE ; } diff --git a/wiringPiD/Makefile b/wiringPiD/Makefile new file mode 100644 index 0000000..6b2cc9e --- /dev/null +++ b/wiringPiD/Makefile @@ -0,0 +1,100 @@ +# +# Makefile: +# The wiringPiD utility: +# https://projects.drogon.net/wiring-pi +# +# Copyright (c) 2012-2017 Gordon Henderson +################################################################################# +# This file is part of wiringPi: +# A "wiring" library for the Raspberry Pi +# +# wiringPi is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# wiringPi is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with wiringPi. If not, see . +################################################################################# + +DESTDIR?=/usr +PREFIX?=/local + +ifneq ($V,1) +Q ?= @ +endif + +#DEBUG = -g -O0 +DEBUG = -O2 +CC = gcc +INCLUDE = -I$(DESTDIR)$(PREFIX)/include +CFLAGS = $(DEBUG) -Wall -Wextra $(INCLUDE) -Winline -pipe + +LDFLAGS = -L$(DESTDIR)$(PREFIX)/lib +LIBS = -lwiringPi -lwiringPiDev -lpthread -lrt -lm -lcrypt + +# May not need to alter anything below this line +############################################################################### + +SRC = wiringpid.c network.c runRemote.c daemonise.c + +OBJ = $(SRC:.c=.o) + +all: wiringpid + +wiringpid: $(OBJ) + $Q echo [Link] + $Q $(CC) -o $@ $(OBJ) $(LDFLAGS) $(LIBS) + +.c.o: + $Q echo [Compile] $< + $Q $(CC) -c $(CFLAGS) $< -o $@ + +.PHONY: clean +clean: + $Q echo "[Clean]" + $Q rm -f $(OBJ) wiringpid *~ core tags *.bak + +.PHONY: tags +tags: $(SRC) + $Q echo [ctags] + $Q ctags $(SRC) + +.PHONY: install +install: wiringpid + $Q echo "[Install]" + $Q mkdir -p $(DESTDIR)$(PREFIX)/sbin + $Q cp wiringpid $(DESTDIR)$(PREFIX)/sbin + $Q chown root.root $(DESTDIR)$(PREFIX)/sbin/wiringpid + +# $Q mkdir -p $(DESTDIR)$(PREFIX)/man/man8 +# $Q cp gpio.1 $(DESTDIR)$(PREFIX)/man/man8 + +.PHONY: install-deb +install-deb: gpio + $Q echo "[Install: deb]" + $Q install -m 0755 -d ~/wiringPi/debian-template/wiringPi/usr/bin + $Q install -m 0755 gpio ~/wiringPi/debian-template/wiringPi/usr/bin + $Q install -m 0755 -d ~/wiringPi/debian-template/wiringPi/man/man1 + $Q install -m 0644 gpio.1 ~/wiringPi/debian-template/wiringPi/man/man1 + +.PHONY: uninstall +uninstall: + $Q echo "[UnInstall]" + $Q rm -f $(DESTDIR)$(PREFIX)/sbin/wiringpid + $Q rm -f $(DESTDIR)$(PREFIX)/man/man8/wiringpid.8 + +.PHONY: depend +depend: + makedepend -Y $(SRC) +# DO NOT DELETE + +wiringpid.o: drcNetCmd.h network.h runRemote.h daemonise.h +network.o: network.h +runRemote.o: drcNetCmd.h network.h runRemote.h +daemonise.o: daemonise.h diff --git a/wiringPiD/daemonise.c b/wiringPiD/daemonise.c new file mode 100644 index 0000000..134a6bb --- /dev/null +++ b/wiringPiD/daemonise.c @@ -0,0 +1,82 @@ +/* + * daemonise.c: + * Fairly generic "Turn the current process into a daemon" code. + * + * Copyright (c) 2016-2017 Gordon Henderson. + ********************************************************************************* + */ + +#include +#include +#include +#include +#include +#include + +#include "daemonise.h" + +void daemonise (const char *pidFile) +{ + pid_t pid ; + int i ; + FILE *fd ; + + syslog (LOG_DAEMON | LOG_INFO, "Becoming daemon") ; + +// Fork from the parent + + if ((pid = fork ()) < 0) + { + syslog (LOG_DAEMON | LOG_ALERT, "Fork no. 1 failed: %m") ; + exit (EXIT_FAILURE) ; + } + + if (pid > 0) // Parent - terminate + exit (EXIT_SUCCESS) ; + +// Now running on the child - become session leader + + if (setsid() < 0) + { + syslog (LOG_DAEMON | LOG_ALERT, "setsid failed: %m") ; + exit (EXIT_FAILURE) ; + } + +// Ignore a few signals + + signal (SIGCHLD, SIG_IGN) ; + signal (SIGHUP, SIG_IGN) ; + +// Fork again + + if ((pid = fork ()) < 0) + { + syslog (LOG_DAEMON | LOG_ALERT, "Fork no. 2 failed: %m") ; + exit (EXIT_FAILURE) ; + } + + if (pid > 0) // parent - terminate + exit (EXIT_SUCCESS) ; + +// Tidying up - reset umask, change to / and close all files + + umask (0) ; + chdir ("/") ; + + for (i = 0 ; i < sysconf (_SC_OPEN_MAX) ; ++i) + close (i) ; + +// Write PID into /var/run + + if (pidFile != NULL) + { + if ((fd = fopen (pidFile, "w")) == NULL) + { + syslog (LOG_DAEMON | LOG_ALERT, "Unable to write PID file: %m") ; + exit (EXIT_FAILURE) ; + } + + fprintf (fd, "%d\n", getpid ()) ; + fclose (fd) ; + } +} diff --git a/wiringPiD/daemonise.h b/wiringPiD/daemonise.h new file mode 100644 index 0000000..8d13319 --- /dev/null +++ b/wiringPiD/daemonise.h @@ -0,0 +1,9 @@ +/* + * daemonise.h: + * Fairly generic "Turn the current process into a daemon" code. + * + * Copyright (c) 2016-2017 Gordon Henderson. + ********************************************************************************* + */ + +extern void daemonise (const char *pidFile) ; diff --git a/wiringPiD/drcNetCmd.h b/wiringPiD/drcNetCmd.h new file mode 100644 index 0000000..23f7dc1 --- /dev/null +++ b/wiringPiD/drcNetCmd.h @@ -0,0 +1,44 @@ +/* + * drcNetCmd.c: + * Copyright (c) 2012-2017 Gordon Henderson + *********************************************************************** + * This file is part of wiringPi: + * https://projects.drogon.net/raspberry-pi/wiringpi/ + * + * wiringPi is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * wiringPi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with wiringPi. If not, see . + *********************************************************************** + */ + +#define DEFAULT_SERVER_PORT 6124 + +#define DRCN_PIN_MODE 1 +#define DRCN_PULL_UP_DN 2 + +#define DRCN_DIGITAL_WRITE 3 +#define DRCN_DIGITAL_WRITE8 4 +#define DRCN_ANALOG_WRITE 5 +#define DRCN_PWM_WRITE 6 + +#define DRCN_DIGITAL_READ 7 +#define DRCN_DIGITAL_READ8 8 +#define DRCN_ANALOG_READ 9 + + +struct drcNetComStruct +{ + uint32_t pin ; + uint32_t cmd ; + uint32_t data ; +} comDat ; + diff --git a/wiringPiD/network.c b/wiringPiD/network.c new file mode 100644 index 0000000..9f6bb88 --- /dev/null +++ b/wiringPiD/network.c @@ -0,0 +1,330 @@ +/* + * network.c: + * Part of wiringPiD + * Copyright (c) 2012-2017 Gordon Henderson + *********************************************************************** + * This file is part of wiringPi: + * https://projects.drogon.net/raspberry-pi/wiringpi/ + * + * wiringPi is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * wiringPi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with wiringPi. If not, see . + *********************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "network.h" + +#define TRUE (1==1) +#define FALSE (!TRUE) + +// Local data + +#define SALT_LEN 16 + +static char salt [SALT_LEN + 1] ; +static char *returnedHash = NULL ; +static int serverFd = -1 ; + +// Union for the server Socket Address + +static union +{ + struct sockaddr_in sin ; + struct sockaddr_in6 sin6 ; +} serverSockAddr ; + +// and client address + +static union +{ + struct sockaddr_in sin ; + struct sockaddr_in6 sin6 ; +} clientSockAddr ; + + +/* + * getClientIP: + * Returns a pointer to a static string containing the clients IP address + ********************************************************************************* + */ + +char *getClientIP (void) +{ + char buf [INET6_ADDRSTRLEN] ; + static char ipAddress [1024] ; + + if (clientSockAddr.sin.sin_family == AF_INET) // IPv4 + { + if (snprintf (ipAddress, 1024, "IPv4: %s", + inet_ntop (clientSockAddr.sin.sin_family, (void *)&clientSockAddr.sin.sin_addr, buf, sizeof (buf))) == 1024) + strcpy (ipAddress, "Too long") ; + } + else // IPv6 + { + if (clientSockAddr.sin.sin_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED (&clientSockAddr.sin6.sin6_addr)) + { + if (snprintf (ipAddress, 1024, "IPv4in6: %s", + inet_ntop (clientSockAddr.sin.sin_family, (char *)&clientSockAddr.sin6.sin6_addr, buf, sizeof(buf))) == 1024) + strcpy (ipAddress, "Too long") ; + } + else + { + if (snprintf (ipAddress, 1024, "IPv6: %s", + inet_ntop (clientSockAddr.sin.sin_family, (char *)&clientSockAddr.sin6.sin6_addr, buf, sizeof(buf))) == 1024) + strcpy (ipAddress, "Too long") ; + } + } + + return ipAddress ; +} + + + +/* + * clientPstr: clientPrintf: + * Print over a network socket + ********************************************************************************* + */ + +static int clientPstr (int fd, char *s) +{ + int len = strlen (s) ; + return (write (fd, s, len) == len) ? 0 : -1 ; +} + +static int clientPrintf (const int fd, const char *message, ...) +{ + va_list argp ; + char buffer [1024] ; + + va_start (argp, message) ; + vsnprintf (buffer, 1023, message, argp) ; + va_end (argp) ; + + return clientPstr (fd, buffer) ; +} + + +/* + * sendGreeting: + * Send some text to the client device + ********************************************************************************* + */ + +int sendGreeting (int clientFd) +{ + if (clientPrintf (clientFd, "200 Welcome to wiringPiD - http://wiringpi.com/\n") < 0) + return -1 ; + + return clientPrintf (clientFd, "200 Connecting from: %s\n", getClientIP ()) ; +} + + +/* + * getSalt: + * Create a random 'salt' value for the password encryption process + ********************************************************************************* + */ + +static int getSalt (char drySalt []) +{ + static const char *seaDog = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789/." ; + + unsigned char wetSalt [SALT_LEN] ; + int i, fd ; + + if ((fd = open ("/dev/urandom", O_RDONLY)) < 0) + return fd ; + + if (read (fd, wetSalt, SALT_LEN) != SALT_LEN) + return -1 ; + + close (fd) ; + + for (i = 0 ; i < SALT_LEN ; ++i) + drySalt [i] = seaDog [wetSalt [i] & 63] ; + + drySalt [SALT_LEN] = 0 ; + + return 0 ; +} + + +/* + * sendChallenge: + * Create and send our salt (aka nonce) to the remote device + ********************************************************************************* + */ + +int sendChallenge (int clientFd) +{ + if (getSalt (salt) < 0) + return -1 ; + + return clientPrintf (clientFd, "Challenge %s\n", salt) ; +} + + +/* + * getResponse: + * Read the encrypted password from the remote device. + ********************************************************************************* + */ + + +int getResponse (int clientFd) +{ + char reply [1024] ; + int len ; + +// Being sort of lazy about this. I'm expecting an SHA-512 hash back and these +// are exactly 86 characters long, so no reason not to, I guess... + + len = 86 ; + + if (setsockopt (clientFd, SOL_SOCKET, SO_RCVLOWAT, (void *)&len, sizeof (len)) < 0) + return -1 ; + + len = recv (clientFd, reply, 86, 0) ; + if (len != 86) + return -1 ; + + reply [len] = 0 ; + + if ((returnedHash = malloc (len + 1)) == NULL) + return -1 ; + + strcpy (returnedHash, reply) ; + + return 0 ; +} + + +/* + * passwordMatch: + * See if there's a match. If not, we simply dump them. + ********************************************************************************* + */ + +int passwordMatch (const char *password) +{ + char *encrypted ; + char salted [1024] ; + + sprintf (salted, "$6$%s$", salt) ; + + encrypted = crypt (password, salted) ; + +// 20: $6$ then 16 characters of salt, then $ +// 86 is the length of an SHA-512 hash + + return strncmp (encrypted + 20, returnedHash, 86) == 0 ; +} + + +/* + * setupServer: + * Do what's needed to create a local server socket instance that can listen + * on both IPv4 and IPv6 interfaces. + ********************************************************************************* + */ + +int setupServer (int serverPort) +{ + socklen_t clientSockAddrSize = sizeof (clientSockAddr) ; + + int on = 1 ; + int family ; + socklen_t serverSockAddrSize ; + int clientFd ; + +// Try to create an IPv6 socket + + serverFd = socket (PF_INET6, SOCK_STREAM, 0) ; + +// If it didn't work, then fall-back to IPv4. + + if (serverFd < 0) + { + if ((serverFd = socket (PF_INET, SOCK_STREAM, 0)) < 0) + return -1 ; + + family = AF_INET ; + serverSockAddrSize = sizeof (struct sockaddr_in) ; + } + else // We got an IPv6 socket + { + family = AF_INET6 ; + serverSockAddrSize = sizeof (struct sockaddr_in6) ; + } + + if (setsockopt (serverFd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) + return -1 ; + +// Setup the servers socket address - cope with IPv4 and v6. + + memset (&serverSockAddr, 0, sizeof (serverSockAddr)) ; + switch (family) + { + case AF_INET: + serverSockAddr.sin.sin_family = AF_INET ; + serverSockAddr.sin.sin_addr.s_addr = htonl (INADDR_ANY) ; + serverSockAddr.sin.sin_port = htons (serverPort) ; + break; + + case AF_INET6: + serverSockAddr.sin6.sin6_family = AF_INET6 ; + serverSockAddr.sin6.sin6_addr = in6addr_any ; + serverSockAddr.sin6.sin6_port = htons (serverPort) ; + } + +// Bind, listen and accept + + if (bind (serverFd, (struct sockaddr *)&serverSockAddr, serverSockAddrSize) < 0) + return -1 ; + + if (listen (serverFd, 4) < 0) // Really only going to talk to one client at a time... + return -1 ; + + if ((clientFd = accept (serverFd, (struct sockaddr *)&clientSockAddr, &clientSockAddrSize)) < 0) + return -1 ; + + return clientFd ; +} + + +/* + * closeServer: + ********************************************************************************* + */ + +void closeServer (int clientFd) +{ + if (serverFd != -1) close (serverFd) ; + if (clientFd != -1) close (clientFd) ; + serverFd = clientFd = -1 ; +} diff --git a/wiringPiD/network.h b/wiringPiD/network.h new file mode 100644 index 0000000..94c3380 --- /dev/null +++ b/wiringPiD/network.h @@ -0,0 +1,31 @@ +/* + * network.h: + * Part of wiringPiD + * Copyright (c) 2012-2017 Gordon Henderson + *********************************************************************** + * This file is part of wiringPi: + * https://projects.drogon.net/raspberry-pi/wiringpi/ + * + * wiringPi is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * wiringPi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with wiringPi. If not, see . + *********************************************************************** + */ + +extern char *getClientIP (void) ; +extern int getResponce (int clientFd) ; +extern int setupServer (int serverPort) ; +extern int sendGreeting (int clientFd) ; +extern int sendChallenge (int clientFd) ; +extern int getResponse (int clientFd) ; +extern int passwordMatch (const char *password) ; +extern void closeServer (int clientFd) ; diff --git a/wiringPiD/runRemote.c b/wiringPiD/runRemote.c new file mode 100644 index 0000000..cd7432b --- /dev/null +++ b/wiringPiD/runRemote.c @@ -0,0 +1,126 @@ +/* + * runRemote.c: + * Run the remote commands passed over the network link. + * + * Copyright (c) 2012-2017 Gordon Henderson + *********************************************************************** + * This file is part of wiringPi: + * https://projects.drogon.net/raspberry-pi/wiringpi/ + * + * wiringPi is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * wiringPi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with wiringPi. If not, see . + *********************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include +//#include + +#include +#include + +#include "drcNetCmd.h" +#include "network.h" +#include "runRemote.h" + + + +int noLocalPins = FALSE ; + + +void runRemoteCommands (int fd) +{ + register uint32_t pin ; + int len ; + struct drcNetComStruct cmd ; + + len = sizeof (struct drcNetComStruct) ; + + if (setsockopt (fd, SOL_SOCKET, SO_RCVLOWAT, (void *)&len, sizeof (len)) < 0) + return ; + + for (;;) + { + if (recv (fd, &cmd, sizeof (cmd), 0) != sizeof (cmd)) // Probably remote hangup + return ; + + pin = cmd.pin ; + if (noLocalPins && ((pin & PI_GPIO_MASK) == 0)) + { + if (send (fd, &cmd, sizeof (cmd), 0) != sizeof (cmd)) + return ; + continue ; + } + + switch (cmd.cmd) + { + case DRCN_PIN_MODE: + pinMode (pin, cmd.data) ; + if (send (fd, &cmd, sizeof (cmd), 0) != sizeof (cmd)) + return ; + break ; + + case DRCN_PULL_UP_DN: + pullUpDnControl (pin, cmd.data) ; + break ; + + case DRCN_PWM_WRITE: + pwmWrite (pin, cmd.data) ; + if (send (fd, &cmd, sizeof (cmd), 0) != sizeof (cmd)) + return ; + break ; + + case DRCN_DIGITAL_WRITE: + digitalWrite (pin, cmd.data) ; + if (send (fd, &cmd, sizeof (cmd), 0) != sizeof (cmd)) + return ; + break ; + + case DRCN_DIGITAL_WRITE8: + //digitalWrite8 (pin, cmd.data) ; + if (send (fd, &cmd, sizeof (cmd), 0) != sizeof (cmd)) + return ; + break ; + + case DRCN_DIGITAL_READ: + cmd.data = digitalRead (pin) ; + if (send (fd, &cmd, sizeof (cmd), 0) != sizeof (cmd)) + return ; + break ; + + case DRCN_DIGITAL_READ8: + //cmd.data = digitalRead8 (pin) ; + if (send (fd, &cmd, sizeof (cmd), 0) != sizeof (cmd)) + return ; + break ; + + case DRCN_ANALOG_WRITE: + analogWrite (pin, cmd.data) ; + if (send (fd, &cmd, sizeof (cmd), 0) != sizeof (cmd)) + return ; + break ; + + case DRCN_ANALOG_READ: + cmd.data = analogRead (pin) ; + if (send (fd, &cmd, sizeof (cmd), 0) != sizeof (cmd)) + return ; + break ; + } + } + +} diff --git a/wiringPiD/runRemote.h b/wiringPiD/runRemote.h new file mode 100644 index 0000000..57d5018 --- /dev/null +++ b/wiringPiD/runRemote.h @@ -0,0 +1,29 @@ +/* + * runRemote.h: + * Run the remote commands passed over the network link. + * + * Copyright (c) 2012-2017 Gordon Henderson + *********************************************************************** + * This file is part of wiringPi: + * https://projects.drogon.net/raspberry-pi/wiringpi/ + * + * wiringPi is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * wiringPi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with wiringPi. If not, see . + *********************************************************************** + */ + +// Globals + +extern int noLocalPins ; + +extern void runRemoteCommands (int fd) ; diff --git a/wiringPiD/wiringpid b/wiringPiD/wiringpid new file mode 100755 index 0000000..b21232e Binary files /dev/null and b/wiringPiD/wiringpid differ diff --git a/wiringPiD/wiringpid.c b/wiringPiD/wiringpid.c new file mode 100644 index 0000000..8dde1cd --- /dev/null +++ b/wiringPiD/wiringpid.c @@ -0,0 +1,382 @@ +/* + * wiringPiD.c: + * Copyright (c) 2012-2017 Gordon Henderson + *********************************************************************** + * This file is part of wiringPi: + * https://projects.drogon.net/raspberry-pi/wiringpi/ + * + * wiringPi is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * wiringPi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with wiringPi. If not, see . + *********************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "drcNetCmd.h" +#include "network.h" +#include "runRemote.h" +#include "daemonise.h" + + +#define PIDFILE "/var/run/wiringPiD.pid" + + +// Globals + +static const char *usage = "[-h] [-d] [-g | -1 | -z] [[-x extension:pin:params] ...] password" ; +static int doDaemon = FALSE ; + +// + +static void logMsg (const char *message, ...) +{ + va_list argp ; + char buffer [1024] ; + + va_start (argp, message) ; + vsnprintf (buffer, 1023, message, argp) ; + va_end (argp) ; + + if (doDaemon) + syslog (LOG_DAEMON | LOG_INFO, "%s", buffer) ; + else + printf ("%s\n", buffer) ; +} + + +/* + * sigHandler: + * setupSigHandler: + * Somehing has happened that would normally terminate the program so try + * to close down nicely. + ********************************************************************************* + */ + +void sigHandler (int sig) +{ + logMsg ("Exiting on signal %d: %s", sig, strsignal (sig)) ; + (void)unlink (PIDFILE) ; + exit (EXIT_FAILURE) ; +} + +void setupSigHandler (void) +{ + struct sigaction action ; + + sigemptyset (&action.sa_mask) ; + action.sa_flags = 0 ; + +// Ignore what we can + + action.sa_handler = SIG_IGN ; + + sigaction (SIGHUP, &action, NULL) ; + sigaction (SIGTTIN, &action, NULL) ; + sigaction (SIGTTOU, &action, NULL) ; + +// Trap what we can to exit gracefully + + action.sa_handler = sigHandler ; + + sigaction (SIGINT, &action, NULL) ; + sigaction (SIGQUIT, &action, NULL) ; + sigaction (SIGILL, &action, NULL) ; + sigaction (SIGABRT, &action, NULL) ; + sigaction (SIGFPE, &action, NULL) ; + sigaction (SIGSEGV, &action, NULL) ; + sigaction (SIGPIPE, &action, NULL) ; + sigaction (SIGALRM, &action, NULL) ; + sigaction (SIGTERM, &action, NULL) ; + sigaction (SIGUSR1, &action, NULL) ; + sigaction (SIGUSR2, &action, NULL) ; + sigaction (SIGCHLD, &action, NULL) ; + sigaction (SIGTSTP, &action, NULL) ; + sigaction (SIGBUS, &action, NULL) ; +} + + +/* + * The works... + ********************************************************************************* + */ + +int main (int argc, char *argv []) +{ + int clientFd ; + char *p, *password ; + int i ; + int port = DEFAULT_SERVER_PORT ; + int wpiSetup = 0 ; + + if (argc < 2) + { + fprintf (stderr, "Usage: %s %s\n", argv [0], usage) ; + exit (EXIT_FAILURE) ; + } + +// Help? + + if (strcasecmp (argv [1], "-h") == 0) + { + printf ("Usage: %s %s\n", argv [0], usage) ; + return 0 ; + } + +// Daemonize? +// Must come before the other args as e.g. some extensions +// open files which get closed on daemonise... + + if (strcasecmp (argv [1], "-d") == 0) + { + if (geteuid () != 0) + { + fprintf (stderr, "%s: Must be root to run as a daemon.\n", argv [0]) ; + exit (EXIT_FAILURE) ; + } + + doDaemon = TRUE ; + daemonise (PIDFILE) ; + + for (i = 2 ; i < argc ; ++i) + argv [i - 1] = argv [i] ; + --argc ; + } + +// Scan all other arguments + + while (*argv [1] == '-') + { + +// Look for wiringPi setup arguments: +// Same as the gpio command and rtb. + +// -g - bcm_gpio + + if (strcasecmp (argv [1], "-g") == 0) + { + if (wpiSetup == 0) + { + logMsg ("BCM_GPIO mode selected") ; + wiringPiSetupGpio () ; + } + + for (i = 2 ; i < argc ; ++i) + argv [i - 1] = argv [i] ; + --argc ; + ++wpiSetup ; + continue ; + } + +// -1 - physical pins + + if (strcasecmp (argv [1], "-1") == 0) + { + if (wpiSetup == 0) + { + logMsg ("GPIO-PHYS mode selected") ; + wiringPiSetupPhys () ; + } + + for (i = 2 ; i < argc ; ++i) + argv [i - 1] = argv [i] ; + --argc ; + ++wpiSetup ; + continue ; + } + +// -z - no wiringPi - blocks remotes accessing local pins + + if (strcasecmp (argv [1], "-z") == 0) + { + if (wpiSetup == 0) + logMsg ("No GPIO mode selected") ; + + for (i = 2 ; i < argc ; ++i) + argv [i - 1] = argv [i] ; + --argc ; + noLocalPins = TRUE ; + ++wpiSetup ; + continue ; + } + +// -p to select the port + + if (strcasecmp (argv [1], "-p") == 0) + { + if (argc < 3) + { + logMsg ("-p missing extension port") ; + exit (EXIT_FAILURE) ; + } + + logMsg ("Setting port to: %s", argv [2]) ; + + port = atoi (argv [2]) ; + if ((port < 1) || (port > 65535)) + { + logMsg ("Invalid server port: %d", port) ; + exit (EXIT_FAILURE) ; + } + +// Shift args down by 2 + + for (i = 3 ; i < argc ; ++i) + argv [i - 2] = argv [i] ; + argc -= 2 ; + + continue ; + } + +// Check for -x argument to load in a new extension +// -x extension:base:args +// Can load many modules to extend the daemon. + + if (strcasecmp (argv [1], "-x") == 0) + { + if (argc < 3) + { + logMsg ("-x missing extension name:data:etc.") ; + exit (EXIT_FAILURE) ; + } + + logMsg ("Loading extension: %s", argv [2]) ; + + if (!loadWPiExtension (argv [0], argv [2], TRUE)) + { + logMsg ("Extension load failed: %s", strerror (errno)) ; + exit (EXIT_FAILURE) ; + } + +// Shift args down by 2 + + for (i = 3 ; i < argc ; ++i) + argv [i - 2] = argv [i] ; + argc -= 2 ; + + continue ; + } + + logMsg ("Invalid parameter: %s", argv [1]) ; + exit (EXIT_FAILURE) ; + } + +// Default to wiringPi mode + + if (wpiSetup == 0) + { + logMsg ("WiringPi GPIO mode selected") ; + wiringPiSetup () ; + } + +// Finally, should just be one arg left - the password... + + if (argc != 2) + { + logMsg ("No password supplied") ; + exit (EXIT_FAILURE) ; + } + + if (strlen (argv [1]) < 6) + { + logMsg ("Password too short - at least 6 chars, not %d", strlen (argv [1])) ; + exit (EXIT_FAILURE) ; + } + + if ((password = malloc (strlen (argv [1]) + 1)) == NULL) + { + logMsg ("Out of memory") ; + exit (EXIT_FAILURE) ; + } + strcpy (password, argv [1]) ; + +// Wipe out the password on the command-line in a vague attempt to try to +// hide it from snoopers + + for (p = argv [1] ; *p ; ++p) + *p = ' ' ; + + setupSigHandler () ; + +// Enter our big loop + + for (;;) + { + + if (!doDaemon) + printf ("-=-\nWaiting for a new connection...\n") ; + + if ((clientFd = setupServer (port)) < 0) + { + logMsg ("Unable to setup server: %s", strerror (errno)) ; + exit (EXIT_FAILURE) ; + } + + logMsg ("New connection from: %s.", getClientIP ()) ; + + if (!doDaemon) + printf ("Sending Greeting.\n") ; + + if (sendGreeting (clientFd) < 0) + { + logMsg ("Unable to send greeting message: %s", strerror (errno)) ; + closeServer (clientFd) ; + continue ; + } + + if (!doDaemon) + printf ("Sending Challenge.\n") ; + + if (sendChallenge (clientFd) < 0) + { + logMsg ("Unable to send challenge message: %s", strerror (errno)) ; + closeServer (clientFd) ; + continue ; + } + + if (!doDaemon) + printf ("Waiting for response.\n") ; + + if (getResponse (clientFd) < 0) + { + logMsg ("Connection closed waiting for response: %s", strerror (errno)) ; + closeServer (clientFd) ; + continue ; + } + + if (!passwordMatch (password)) + { + logMsg ("Password failure") ; + closeServer (clientFd) ; + continue ; + } + + logMsg ("Password OK - Starting") ; + + runRemoteCommands (clientFd) ; + closeServer (clientFd) ; + } + + return 0 ; +}