diff --git a/MakeRemote b/MakeRemote new file mode 100644 index 0000000..e0f6361 --- /dev/null +++ b/MakeRemote @@ -0,0 +1,47 @@ +CC = gcc +AR = ar +RANLIB = ranlib +SIZE = size + +CFLAGS = -O3 -Wall + +ALL = libpigpiod_if.a pigs + +all: $(ALL) + +pigs: command.o + $(CC) -o pigs pigs.c command.c + +clean: + rm -f *.o *.i *.s *~ $(ALL) + +install: $(LIB) + sudo install -m 0755 -d /usr/local/include + sudo install -m 0644 pigpio.h /usr/local/include + sudo install -m 0644 pigpiod_if.h /usr/local/include + sudo install -m 0755 -d /usr/local/lib + sudo install -m 0644 libpigpiod_if.a /usr/local/lib + sudo install -m 0755 -d /usr/local/bin + sudo install -m 0755 pigs /usr/local/bin + sudo python setup.py install + +uninstall: + sudo rm -f /usr/local/include/pigpio.h + sudo rm -f /usr/local/lib/libpigpiod_if.a + sudo rm -f /usr/local/bin/pigs + +LIB = libpigpiod_if.a +OBJ = pigpiod_if.o command.o + +$(LIB): $(OBJ) + $(AR) rcs $(LIB) $(OBJ) + $(RANLIB) $(LIB) + $(SIZE) $(LIB) + +# generated using gcc -MM *.c + +command.o: command.c pigpio.h command.h +pigpiod.o: pigpiod.c pigpio.h command.h +pigpiod_if.o: pigpiod_if.c pigpio.h pigpiod_if.h +pigs.o: pigs.c pigpio.h command.h + diff --git a/Makefile b/Makefile index 8684ac6..6ce0b3d 100644 --- a/Makefile +++ b/Makefile @@ -5,61 +5,74 @@ SIZE = size CFLAGS = -O3 -Wall -ALL = libpigpio.a checklib demolib pig2vcd pigpiod pigs +LIB1 = libpigpio.a +OBJ1 = pigpio.o command.o + +LIB2 = libpigpiod_if.a +OBJ2 = pigpiod_if.o command.o + +LIB = $(LIB1) $(LIB2) + +ALL = $(LIB) checklib pig2vcd pigpiod pigs + +LL = -L. -lpigpio -lpthread -lrt all: $(ALL) -checklib: checklib.o libpigpio.a - $(CC) -o checklib checklib.c -L. -lpigpio -lpthread -lrt +checklib: checklib.o $(LIB1) + $(CC) -o checklib checklib.c $(LL) -demolib: demolib.o libpigpio.a - $(CC) -o demolib demolib.c -L. -lpigpio -lpthread -lrt +pigpiod: pigpiod.o $(LIB1) + $(CC) -o pigpiod pigpiod.c $(LL) + +pigs: pigs.o command.o + $(CC) -o pigs pigs.c command.c pig2vcd: pig2vcd.o $(CC) -o pig2vcd pig2vcd.c -pigpiod: pigpiod.o libpigpio.a - $(CC) -o pigpiod pigpiod.c -L. -lpigpio -lpthread -lrt - -pigs: command.o - $(CC) -o pigs pigs.c command.c - clean: rm -f *.o *.i *.s *~ $(ALL) install: $(LIB) - sudo install -m 0755 -d /usr/local/include - sudo install -m 0644 pigpio.h /usr/local/include - sudo install -m 0755 -d /usr/local/lib - sudo install -m 0644 libpigpio.a /usr/local/lib - sudo install -m 0755 -d /usr/local/bin - sudo install -m 0755 pig2vcd /usr/local/bin - sudo install -m 0755 pigpiod /usr/local/bin - sudo install -m 0755 pigs /usr/local/bin + sudo install -m 0755 -d /usr/local/include + sudo install -m 0644 pigpio.h /usr/local/include + sudo install -m 0644 pigpiod_if.h /usr/local/include + sudo install -m 0755 -d /usr/local/lib + sudo install -m 0644 libpigpio.a /usr/local/lib + sudo install -m 0644 libpigpiod_if.a /usr/local/lib + sudo install -m 0755 -d /usr/local/bin + sudo install -m 0755 pig2vcd /usr/local/bin + sudo install -m 0755 pigpiod /usr/local/bin + sudo install -m 0755 pigs /usr/local/bin sudo python setup.py install uninstall: sudo rm -f /usr/local/include/pigpio.h + sudo rm -f /usr/local/include/pigpiod_if.h sudo rm -f /usr/local/lib/libpigpio.a + sudo rm -f /usr/local/lib/libpigpiod_if.a sudo rm -f /usr/local/bin/pig2vcd sudo rm -f /usr/local/bin/pigpiod sudo rm -f /usr/local/bin/pigs -LIB = libpigpio.a -OBJ = pigpio.o command.o +$(LIB1): $(OBJ1) + $(AR) rcs $(LIB1) $(OBJ1) + $(RANLIB) $(LIB1) + $(SIZE) $(LIB1) -$(LIB): $(OBJ) - $(AR) rcs $(LIB) $(OBJ) - $(RANLIB) $(LIB) - $(SIZE) $(LIB) +$(LIB2): $(OBJ2) + $(AR) rcs $(LIB2) $(OBJ2) + $(RANLIB) $(LIB2) + $(SIZE) $(LIB2) -# generated using gcc -M *.c +# generated using gcc -MM *.c checklib.o: checklib.c pigpio.h command.o: command.c pigpio.h command.h -demolib.o: demolib.c pigpio.h pig2vcd.o: pig2vcd.c pigpio.h pigpio.o: pigpio.c pigpio.h command.h pigpiod.o: pigpiod.c pigpio.h command.h +pigpiod_if.o: pigpiod_if.c pigpio.h command.h pigpiod_if.h pigs.o: pigs.c pigpio.h command.h diff --git a/README b/README index 86e7ba0..ba15cb6 100644 --- a/README +++ b/README @@ -9,13 +9,16 @@ Enter the following two commands (in this order) make make install -This will install: - the library (libpigpio.a) in /usr/local/lib - the header file (pigpio.h) in /usr/local/include - the daemon (pigpiod) in /usr/local/bin - the socket interface (pigs) in /usr/local/bin - the utility pig2vcd in /usr/local/bin - the Python module pigpio.py +This will install + +o the library (libpigpio.a) in /usr/local/lib +o the library (libpigpiod_if.a) in /usr/local/lib +o the header file (pigpio.h) in /usr/local/include +o the header file (pigpiod_if.h) in /usr/local/include +o the daemon (pigpiod) in /usr/local/bin +o the socket interface (pigs) in /usr/local/bin +o the utility pig2vcd in /usr/local/bin +o the Python module pigpio.py TEST @@ -23,8 +26,13 @@ To test the library do sudo ./checklib -checklib.c, demolib.c, pig2vcd.c, pigpiod.c, pigs.c, and pigpio.py -show examples of interfacing with the library. +EXAMPLE CODE + +checklib.c, pig2vcd.c, and pigpiod.c +show examples of interfacing with the pigpio library. + +pigs.c, pigpio.py, and test_pigpiod_if.c +show examples of interfacing with the pigpiod daemon. DAEMON @@ -39,7 +47,7 @@ When the library starts it locks /var/run/pigpio.pid -The file should be deleted when the library terminates. +The file should be automatically deleted when the library terminates. SOCKET INTERFACE @@ -62,16 +70,16 @@ echo "help" >/dev/pigpio PYTHON MODULE -By default the Python pigpio module is installed to the -default python location. You can install it for additional -Python versions by +The Python pigpio module is installed to the default python location. + +You can install it for additional Python versions by pythonx.y setup.py install where x.y is the Python version. -If the pigpiod daemon is running you can test the Python -module by entering the following commands. +If the pigpiod daemon is running you can test the Python module +by entering the following commands. python @@ -95,3 +103,37 @@ To stop the pigpiod daemon sudo killall pigpiod +RUNNING ON NON Pi's + +You can access the pigpiod daemon running on the Pi from any machine which +can access it over the network. This access is via the socket interface. + +In particular this allows you to use the following on non-Pi's. + +o pigs +o the pigpio Python module +o the C socket I/F using libpigpiod_if (header file pigpiod_if.h) + +On a Linux machine + +make -f MakeRemote clean +make -f MakeRemote +make -f MakeRemote install + +This will install + +o the library (libpigpiod_if.a) in /usr/local/lib +o the header file (pigpio.h) in /usr/local/include +o the header file (pigpiod_if.h) in /usr/local/include +o the socket interface (pigs) in /usr/local/bin +o the Python module pigpio.py + +On Windows machines (and possibly Macs) + +The Python module should install with + +python setup.py install + +pigs and pigpiod_if.c will need minor mods to reflect the +Window's/Mac's socket interface. + diff --git a/command.c b/command.c index c3eba85..80b3801 100644 --- a/command.c +++ b/command.c @@ -26,7 +26,7 @@ For more information, please refer to */ /* -This version is for pigpio version 7+ +This version is for pigpio version 10+ */ #include @@ -74,6 +74,7 @@ cmdInfo_t cmdInfo[]= {PI_CMD_TICK, "T", 1, 4}, {PI_CMD_HELP, "HELP", 6, 5}, {PI_CMD_HELP, "H", 6, 5}, + {PI_CMD_PIGPV, "PIGPV", 1, 4}, }; char * cmdUsage = "\ @@ -93,6 +94,7 @@ NC h close notification\n\ PWM/P u d set PWM value for gpio\n\ PFS u d set PWM frequency for gpio\n\ PFG u get PWM frequency for gpio\n\ +PIGPV return pigpio version\n\ PRS u d set PWM range for gpio\n\ PRG u get PWM range for gpio\n\ PRRG u get PWM real range for gpio\n\ @@ -159,6 +161,8 @@ static errInfo_t errInfo[]= {PI_TOO_MANY_PULSES , "waveform has too many pulses"}, {PI_TOO_MANY_CHARS , "waveform has too many chars"}, {PI_NOT_SERIAL_GPIO , "no serial read in progress on gpio"}, + {PI_BAD_SERIAL_STRUC , "bad (null) serial structure parameter"}, + {PI_BAD_SERIAL_BUF , "bad (null) serial buf parameter"}, {PI_NOT_PERMITTED , "no permission to update gpio"}, {PI_SOME_PERMITTED , "no permission to update one or more gpios"}, }; @@ -200,7 +204,7 @@ int cmdParse(char * buf, cmdCmd_t * cmd) switch (cmdInfo[idx].vt) { - case 1: /* BR1 BR2 HWVER NO TICK */ + case 1: /* BR1 BR2 HWVER PIGPV NO TICK */ f = sscanf(buf, " %7s %c", str, &t); if (f == 1) valid = 1; break; diff --git a/demolib.c b/demolib.c deleted file mode 100644 index d2e9bd4..0000000 --- a/demolib.c +++ /dev/null @@ -1,673 +0,0 @@ -/* -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any -means. - -In jurisdictions that recognize copyright laws, the author or authors -of this software dedicate any and all copyright interest in the -software to the public domain. We make this dedication for the benefit -of the public at large and to the detriment of our heirs and -successors. We intend this dedication to be an overt act of -relinquishment in perpetuity of all present and future rights to this -software under copyright law. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -For more information, please refer to -*/ - -/* -This version is for pigpio version 3+ -*/ - -#include -#include -#include -#include -#include -#include -#include - -#include "pigpio.h" - -/* =========================================================================== -THIS PROGRAM NEEDS THE I2C DEVICE AND DEVELOPMENT LIBRARY - -TO GET THE NEEDED FILES DO - -sudo apt-get install libi2c-dev - -BEFORE RUNNING THE PROGRAM ENSURE THAT THE I2C DEVICE IS PRESENT - -sudo modprobe i2c-bcm2708 -sudo modprobe i2c-dev -sudo chmod o+rw /dev/i2c* -=========================================================================== */ - -/* - -P1 Name gpio used for - - 3 SDA 0/2 i2c - 5 SCL 1/3 i2c - 7 --- 4 LASER - 8 TXD 14 LED1 -10 RXD 15 LED2 -11 --- 17 SERVO 1 -12 --- 18 SERVO 2 -13 --- 21/27 SERVO 3 -15 --- 22 LED3 -16 --- 23 TI Launchpad -18 --- 24 Sonar trigger -19 MOSI 10 Sonar echo -21 MISO 9 Motor B In 1 -22 --- 25 LDR -23 SCLK 11 Motor B In 2 -24 CE0 8 Motor A In1 -26 CE1 7 Motor A In2 - -*/ - -#define LASER 4 -#define MOTOR_A_IN2 7 -#define MOTOR_A_IN1 8 -#define MOTOR_B_IN1 9 -#define SONAR_ECHO 10 -#define MOTOR_B_IN2 11 -#define LED1 14 -#define LED2 15 -#define SERVO1 17 -#define SERVO2 18 -#define SERVO3 21 -#define LED3 22 -#define LAUNCHPAD 23 -#define SONAR_TRIGGER 24 -#define LDR 25 - -#define LEDS 4 - -short rawAcc[3]; -short rawGyr[3]; -short rawMag[3]; - -#define ROLL 0 -#define PITCH 1 -#define YAW 2 - -#define ACC_ORIENTATION(X, Y, Z) \ - {rawAcc[ROLL] = -X; rawAcc[PITCH] = -Y; rawAcc[YAW] = Z;} - -#define GYRO_ORIENTATION(X, Y, Z) \ - {rawGyr[ROLL] = Y; rawGyr[PITCH] = -X; rawGyr[YAW] = -Z;} - -#define MAG_ORIENTATION(X, Y, Z) \ - {rawMag[ROLL] = X; rawMag[PITCH] = Y; rawMag[YAW] = -Z;} - - -#define CALIBRATIONS 200 - -#define ADXL345_I2C_ADDR 0x53 -#define ITG3200_I2C_ADDR 0x68 - -static int version, micros=5, millis=100; - -static volatile unsigned long launchpadPulses; -static volatile unsigned long launchpad5; -static volatile unsigned long launchpad10; -static volatile unsigned long launchpad15; -static volatile unsigned long launchpadOutRange; -static volatile int launchpadErr; -static volatile uint32_t LDRrechargeTick; - - -/* forward prototypes */ - -void LEDlaserTick (void); -void motorTick(void); -void i2cTick (void); -void servoTick (void); -void sonarLDRtick(void); - -void launchpadAlert(int gpio, int level, uint32_t tick); -void sonarAlert(int gpio, int level, uint32_t tick); -void LDRalert(int gpio, int level, uint32_t tick); - -void putTTY(char * buf); -void putTTYstr(int row, int col, char * buf); - - -int main(int argc, char *argv[]) -{ - char str[256]; - - if (argc > 1) micros = atoi(argv[1]); - - if (argc > 2) millis = atoi(argv[2]); - - putTTY("\033c"); /* clear console */ - - gpioCfgBufferSize(millis); - - gpioCfgClock(micros, PI_CLOCK_PCM, PI_CLOCK_PLLD); - - - /* before using the library you must call gpioInitialise */ - - version = gpioInitialise(); - - if (version >= 0) - { - /* initialise pins, only gpio numbers are supported */ - - gpioSetMode(SERVO1, PI_OUTPUT); - gpioSetMode(SERVO2, PI_OUTPUT); - gpioSetMode(SERVO3, PI_OUTPUT); - gpioSetMode(LASER, PI_OUTPUT); - gpioSetMode(LED1, PI_OUTPUT); - gpioSetMode(LED2, PI_OUTPUT); - gpioSetMode(LED3, PI_OUTPUT); - gpioSetMode(MOTOR_A_IN1, PI_OUTPUT); - gpioSetMode(MOTOR_A_IN2, PI_OUTPUT); - gpioSetMode(MOTOR_B_IN1, PI_OUTPUT); - gpioSetMode(MOTOR_B_IN2, PI_OUTPUT); - - gpioSetMode(SONAR_TRIGGER, PI_OUTPUT); - gpioWrite (SONAR_TRIGGER, PI_OFF); - - gpioSetMode(SONAR_ECHO, PI_INPUT); - gpioSetMode(LAUNCHPAD, PI_INPUT); - gpioSetMode(LDR, PI_INPUT); - - /* update i2c fifty times a second, timer #0 */ - - gpioSetTimerFunc(0, 20, i2cTick); - - //gpioSetTimerFunc(0, 1000, servoTick); - - /* update LEDs and laser once a second, timer #1 */ - - gpioSetTimerFunc(1, 1000, LEDlaserTick); - - /* update motors every three seconds, timer #2 */ - - gpioSetTimerFunc(2, 3000, motorTick); - - /* update sonar/LDR 10 times a second, timer #3 */ - - gpioSetTimerFunc(3, 100, sonarLDRtick); - - /* an attachecd TI launchpad is transmitting high pulses of - 15, 35, 55, 75, ..., 975, 995 microseconds repeating with 50 - microseconds off between each pulse */ - - gpioSetAlertFunc(LAUNCHPAD, launchpadAlert); - - /* monitor sonar echos */ - - gpioSetAlertFunc(SONAR_ECHO, sonarAlert); - - /* monitor LDR level changes */ - - gpioSetAlertFunc(LDR, LDRalert); - - while (1) - { - sleep(1); - - sprintf(str, "TI pulses %8ld", launchpadPulses); - putTTYstr(9, 1, str); - - sprintf(str, "+/-5 %8ld", launchpad5); - putTTYstr(10, 6, str); - - sprintf(str, "+/-10 %8ld", launchpad10); - putTTYstr(11, 5, str); - - sprintf(str, "+/-15 %8ld", launchpad15); - putTTYstr(12, 5, str); - - sprintf(str, "Others %8ld (last %d) ", - launchpadOutRange, launchpadErr); - putTTYstr(13, 4, str); - } - } - - gpioTerminate(); - - return 0; -} - -void LEDlaserTick(void) -{ - static int gpio[LEDS]={LED1, LED2, LED3, LASER}; - static int pos [LEDS]={ 0, 3, 6, 9}; - static int inc [LEDS]={ 1, 1, 1, 1}; - - static int vals[] = {0, 1, 2, 4, 8, 16, 32, 64, 128, 249}; - - int i; - - for (i=0; i=(sizeof(vals)/4)) || (pos[i]<0) ) - { - inc[i] = -inc[i]; - pos[i] += inc[i]; - } - } -} - -void sonarLDRtick(void) -{ - /* trigger a sonar reading */ - - gpioWrite(SONAR_TRIGGER, PI_ON); - usleep(20); - gpioWrite(SONAR_TRIGGER, PI_OFF); - - /* trigger a LDR reading */ - - gpioSetMode(LDR, PI_OUTPUT); /* drain capacitor */ - - gpioWrite(LDR, PI_OFF); - - usleep(200); - - LDRrechargeTick = gpioTick(); - - gpioSetMode(LDR, PI_INPUT); /* start capacitor recharge */ - } - -void motorTick(void) -{ - static int gpio_in1[2]={MOTOR_A_IN1, MOTOR_B_IN1}; - static int gpio_in2[2]={MOTOR_A_IN2, MOTOR_B_IN2}; - static int speed [2]={ 80, 80}; - static int inc [2]={ -50, 50}; - - int i; - char str[256]; - - for (i=0; i<2; i++) - { - speed[i]+=inc[i]; - - if (speed[i]<0) - { - gpioPWM(gpio_in1[i], -speed[i]); - gpioPWM(gpio_in2[i], 0); - if (speed[i] < -205) inc[i] = -inc[i]; - sprintf(str, "MOT%d IN1=%3d IN2=%3d", i+1, -speed[i], 0); - } - else - { - gpioPWM(gpio_in2[i], speed[i]); - gpioPWM(gpio_in1[i], 0); - if (speed[i] > 205) inc[i] = -inc[i]; - sprintf(str, "MOT%d IN1=%3d IN2=%3d", i+1, 0, speed[i]); - } - if (i) putTTYstr(7, 1, str); else putTTYstr(5, 1, str); - } -} - -/* loads of code to read/write i2c */ - -void selectDevice(int i2c, int addr, char * name) -{ - if (ioctl(i2c, I2C_SLAVE, addr) < 0) - { - fprintf(stderr, "%s not present\n", name); - } -} - -void writeToDevice(int i2c, char * buf, int len) -{ - static int reported = 0; - if (write(i2c, buf, len) != len) - { - if (!reported) - { - fprintf(stderr, "Can't write to device\n"); - reported = 1; - } - } - else reported = 0; -} - -void readADXL345(int i2c) -{ - char buf[8]; - static int reported = 0; - - selectDevice(i2c, ADXL345_I2C_ADDR, "ADXL345"); - - writeToDevice(i2c, "\x32", 1); - - if (read(i2c, buf, 6) != 6) - { - if (!reported) - { - fprintf(stderr, "Unable to read from ADXL345\n"); - reported = 1; - } - } - else - { - reported = 0; - - ACC_ORIENTATION ( - ((buf[1]<<8) | buf[0]), - ((buf[3]<<8) | buf[2]), - ((buf[5]<<8) | buf[4]) ); - } -} - -void readITG3200(int i2c) -{ - char buf[8]; - static int reported = 0; - - selectDevice(i2c, ITG3200_I2C_ADDR, "ITG3200"); - - writeToDevice(i2c, "\x1D", 1); - - if (read(i2c, buf, 6) != 6) - { - if (!reported) - { - fprintf(stderr, "Unable to read from ITG3200\n"); - reported = 1; - } - } - else - { - reported = 0; - - GYRO_ORIENTATION ( - ((buf[0]<<8) | buf[1]), - ((buf[2]<<8) | buf[3]), - ((buf[4]<<8) | buf[5]) ); - } -} - -int initI2Cdevices(void) -{ - int i2c; - - if ((i2c = open("/dev/i2c-0", O_RDWR)) < 0) - { - perror("Failed to open i2c bus"); - exit(1); - } - - /* initialise ADXL345 */ - - selectDevice(i2c, ADXL345_I2C_ADDR, "ADXL345"); - - writeToDevice(i2c, "\x2d\x00", 2); - writeToDevice(i2c, "\x2d\x10", 2); - writeToDevice(i2c, "\x2d\x08", 2); - writeToDevice(i2c, "\x31\x00", 2); - writeToDevice(i2c, "\x31\x0b", 2); - - /* initialise ITG3200 */ - - selectDevice(i2c, ITG3200_I2C_ADDR, "ITG3200"); - - writeToDevice(i2c, "\x16\b00011000", 2); - - return i2c; -} - -/* an attached IMU (GY-85) supplies orientation information which - is used to position the servos */ - -float estimateAngle(int acc, int gyro, float oldAng, int elapsed) -{ - float angleAcc, angleInc, estAngle; - float secs; - - secs = (float) elapsed / 1e6f; - - angleAcc = (float) acc * 90.0f / 256.0f; - - angleInc = (float) gyro * secs * 2000.0f / 32768.0f; - - estAngle = 0.75 * (oldAng + angleInc) + 0.25 * angleAcc; - - return estAngle; -} - -void servoTick(void) -{ - static int wid1=1500, wid2=1500, wid3=1500; - static int inc1=50, inc2=75, inc3=100; - - gpioServo(SERVO1, wid1); - gpioServo(SERVO2, wid2); - gpioServo(SERVO3, wid3); - - wid1+=inc1; if ((wid1<1000) || (wid1>2000)) {inc1 = -inc1; wid1+=inc1;} - wid2+=inc2; if ((wid2<1000) || (wid2>2000)) {inc2 = -inc2; wid2+=inc2;} - wid3+=inc3; if ((wid3<1000) || (wid3>2000)) {inc3 = -inc3; wid3+=inc3;} -} - -void i2cTick(void) -{ - static int inited = 0; - static int calibrated = 0; - static int calibrations = 0; - static int accCalibX = 0, accCalibY = 0, accCalibZ = 0; - static int gyroCalibX = 0, gyroCalibY = 0, gyroCalibZ = 0; - static int i2c; - static float X=0.0, Y=0.0, Z=0.0; - - static uint32_t lastTick; - - uint32_t tick; - int elapsed; - int pulse; - char str[256]; - - if (inited) - { - tick = gpioTick(); - elapsed = tick - lastTick; - lastTick = tick; - - readADXL345(i2c); - readITG3200(i2c); - - if (calibrated) - { - X = estimateAngle( - rawAcc[ROLL], rawGyr[ROLL] -gyroCalibX, X, elapsed); - - Y = estimateAngle( - rawAcc[PITCH], rawGyr[PITCH] - gyroCalibY, Y, elapsed); - - Z = estimateAngle( - rawAcc[YAW], rawGyr[YAW] - gyroCalibZ, Z, elapsed); - - pulse = 1500 + (Y * 1000 / 90); - if (pulse < 500) pulse = 500; - if (pulse > 2500) pulse = 2500; - gpioServo(SERVO1, pulse); - - pulse = 1500 - (X * 500 / 90); - if (pulse < 1000) pulse = 1000; - if (pulse > 2000) pulse = 2000; - gpioServo(SERVO2, pulse); - - /* prefer Z but that doesn't change much */ - pulse = 1500 - (Y * 500 / 90); - if (pulse < 800) pulse = 800; - if (pulse > 2200) pulse = 2200; - gpioServo(SERVO3, pulse); - - sprintf(str, "X=%4.0f Y=%4.0f Z=%4.0f ", X, Y, Z); - putTTYstr(1, 1, str); - } - else - { - accCalibX+=rawAcc[ROLL]; - accCalibY+=rawAcc[PITCH]; - accCalibZ+=rawAcc[YAW]; - - gyroCalibX+=rawGyr[ROLL]; - gyroCalibY+=rawGyr[PITCH]; - gyroCalibZ+=rawGyr[YAW]; - - if (++calibrations >= CALIBRATIONS) - { - accCalibX /= CALIBRATIONS; - accCalibY /= CALIBRATIONS; - accCalibZ /= CALIBRATIONS; - - gyroCalibX /= CALIBRATIONS; - gyroCalibY /= CALIBRATIONS; - gyroCalibZ /= CALIBRATIONS; - - calibrated = 1; - } - } - } - else - { - i2c = initI2Cdevices(); - - gpioServo(SERVO1, 1500); - gpioServo(SERVO2, 1500); - gpioServo(SERVO3, 1500); - - inited = 1; - } -} - -void sonarAlert(int gpio, int level, uint32_t tick) -{ - static uint32_t startTick; - - int diffTick; - char str[256]; - - if (level == PI_ON) - { - startTick = tick; - } - else if (level == PI_OFF) - { - diffTick = tick - startTick; - - if (diffTick < 26100) - { - sprintf(str, "Sonar %3d cms", (diffTick+29)/58); - putTTYstr(15, 1, str); - } - } -} - -void LDRalert(int pin, int level, uint32_t tick) -{ - int diffTick; - char str[256]; - - if (level == PI_ON) - { - diffTick = tick - LDRrechargeTick; - - sprintf(str, "LDR %4d micros", diffTick); - putTTYstr(17, 1, str); - } -} - -void launchpadAlert(int pin, int level, uint32_t tick) -{ - static int inited = 0, lastTick, lastPulseLen; - - int pulseLen, pulseDif; - - if (inited) - { - pulseLen = tick - lastTick; - lastTick = tick; - - if (level==0) - { - if (lastPulseLen) - { - pulseDif = pulseLen - lastPulseLen; - - /* allow for wrap around */ - if (pulseDif < 0) pulseDif += 1096; - - /* now centre around expected value */ - pulseDif -= 33; - - if (pulseDif < 0) pulseDif = -pulseDif; - - launchpadPulses++; - - if (pulseDif <= 5) - { - launchpad5++; - } - else if (pulseDif <= 10) - { - launchpad10++; - } - else if (pulseDif <= 15) - { - launchpad15++; - } - else - { - launchpadOutRange++; - launchpadErr = pulseDif; - } - } - lastPulseLen = pulseLen; - } - } - else - { - lastTick = tick; - lastPulseLen = 0; - - launchpadPulses = 0; - launchpad5 = 0; - launchpad10 = 0; - launchpad15 = 0; - launchpadOutRange = 0; - - inited = 1; - } -} - -void putTTY(char * buf) -{ - write(1, buf, strlen(buf)); -} - -void putTTYstr(int row, int col, char * buf) -{ - char str[256]; - - sprintf(str, "\033[%d;%dH%s", row, col, buf); - - putTTY(str); -} - diff --git a/pigpio.c b/pigpio.c index de8f142..510b45a 100644 --- a/pigpio.c +++ b/pigpio.c @@ -25,7 +25,7 @@ OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to */ -/* pigpio version 9 */ +/* pigpio version 10 */ #include #include @@ -198,6 +198,31 @@ bit 0 READ_LAST_NOT_SET_ERROR } \ while (0) +#define CHECK_INITED_RET_NULL_PTR \ + do \ + { \ + if (!libInitialised) \ + { \ + fprintf(stderr, \ + "%s %s: pigpio uninitialised, call gpioInitialise()\n",\ + myTimeStamp(), __FUNCTION__); \ + return (NULL); \ + } \ + } \ + while (0) + +#define CHECK_INITED_RET_NIL \ + do \ + { \ + if (!libInitialised) \ + { \ + fprintf(stderr, \ + "%s %s: pigpio uninitialised, call gpioInitialise()\n",\ + myTimeStamp(), __FUNCTION__); \ + } \ + } \ + while (0) + #define CHECK_NOT_INITED \ do \ { \ @@ -322,16 +347,16 @@ bit 0 READ_LAST_NOT_SET_ERROR /* DMA CS Control and Status bits */ #define DMA_CHANNEL_RESET (1<<31) #define DMA_WAIT_ON_WRITES (1<<28) -#define DMA_PANIC_PRIORITY(x) ((x)<<20) -#define DMA_PRIORITY(x) ((x)<<16) +#define DMA_PANIC_PRIORITY(x) ((x)<<20) +#define DMA_PRIORITY(x) ((x)<<16) #define DMA_INTERRUPT_STATUS (1<< 2) #define DMA_END_FLAG (1<< 1) #define DMA_ACTIVATE (1<< 0) /* DMA control block "info" field bits */ #define DMA_NO_WIDE_BURSTS (1<<26) -#define DMA_PERIPHERAL_MAPPING(x) ((x)<<16) -#define DMA_BURST_LENGTH(x) ((x)<<12) +#define DMA_PERIPHERAL_MAPPING(x) ((x)<<16) +#define DMA_BURST_LENGTH(x) ((x)<<12) #define DMA_SRC_IGNORE (1<<11) #define DMA_SRC_DREQ (1<<10) #define DMA_SRC_INC (1<< 8) @@ -981,7 +1006,8 @@ static void myDoCommand(cmdCmd_t * cmd) if (gpioMask & (uint64_t)(1<res = res; @@ -4731,6 +4767,56 @@ int gpioSetTimerFuncEx(unsigned id, unsigned ms, gpioTimerFuncEx_t f, return 0; } +/* ----------------------------------------------------------------------- */ + +pthread_t *gpioStartThread(ThreadFunc_t func, void *arg) +{ + pthread_t *pth; + pthread_attr_t pthAttr; + + DBG(DBG_USER, "func=%08X, arg=%08X", (uint32_t)func, (uint32_t)arg); + + CHECK_INITED_RET_NULL_PTR; + + pth = malloc(sizeof(pthread_t)); + + if (pth) + { + if (pthread_attr_init(&pthAttr)) + { + free(pth); + SOFT_ERROR(NULL, "pthread_attr_init failed"); + } + + if (pthread_attr_setstacksize(&pthAttr, STACK_SIZE)) + { + free(pth); + SOFT_ERROR(NULL, "pthread_attr_setstacksize failed"); + } + + if (pthread_create(pth, &pthAttr, func, arg)) + { + free(pth); + SOFT_ERROR(NULL, "pthread_create failed"); + } + } + return pth; +} + +/* ----------------------------------------------------------------------- */ + +void gpioStopThread(pthread_t *pth) +{ + DBG(DBG_USER, "pth=%08X", (uint32_t)pth); + + CHECK_INITED_RET_NIL; + + if (pth) + { + pthread_cancel(*pth); + pthread_join(*pth, NULL); + } +} /* ----------------------------------------------------------------------- */ @@ -4959,6 +5045,16 @@ uint32_t gpioTick(void) } +/* ----------------------------------------------------------------------- */ + +unsigned gpioVersion(void) +{ + DBG(DBG_USER, ""); + + return PIGPIO_VERSION; +} + + /* ----------------------------------------------------------------------- */ unsigned gpioHardwareRevision(void) diff --git a/pigpio.h b/pigpio.h index 9b46ead..f4faaa0 100644 --- a/pigpio.h +++ b/pigpio.h @@ -26,7 +26,7 @@ For more information, please refer to */ /* -This version is for pigpio version 9 +This version is for pigpio version 10 */ #ifndef PIGPIO_H @@ -51,6 +51,7 @@ This version is for pigpio version 9 / output gpio level changes. / / 9) rudimentary permission control through the socket and pipe interfaces / / so users can be prevented from "updating" inappropriate gpios. / +/ 10) a simple interface to start and stop new threads. / / / / NOTE: / / / @@ -83,8 +84,9 @@ This version is for pigpio version 9 ****************************************************************************/ #include +#include -#define PIGPIO_VERSION 9 +#define PIGPIO_VERSION 10 /*-------------------------------------------------------------------------*/ @@ -154,6 +156,9 @@ gpioSetGetSamplesFuncEx Requests a gpio samples callback, extended. gpioSetTimerFunc Request a regular timed callback. gpioSetTimerFuncEx Request a regular timed callback, extended. +gpioStartThread Start a new thread. +gpioStopThread Stop a previously started thread. + gpioSetSignalFunc Request a signal callback. gpioSetSignalFuncEx Request a signal callback, extended. @@ -175,6 +180,8 @@ gpioTick Get current tick (microseconds). gpioHardwareRevision Get hardware version. +gpioVersion Get the pigpio version. + gpioCfgBufferSize Configure the gpio sample buffer size. gpioCfgClock Configure the gpio sample rate. gpioCfgDMAchannel Configure the DMA channel (DEPRECATED). @@ -264,11 +271,15 @@ typedef void (*gpioGetSamplesFuncEx_t) (const gpioSample_t * samples, int numSamples, void * userdata); +typedef void *(ThreadFunc_t) (void *); + + + /* All the functions which return an int return < 0 on error. - If the library isn't initialised all but the gpioCfg* functions - will return error PI_NOT_INITIALISED. + If the library isn't initialised all but the gpioCfg*, gpioVersion, + and gpioHardwareRevision functions will return error PI_NOT_INITIALISED. If the library is initialised the gpioCfg* functions will return error PI_INITIALISED. @@ -381,9 +392,9 @@ int gpioSetPullUpDown(unsigned gpio, /* pud: 0-2 */ -#define PI_PUD_OFF 0 -#define PI_PUD_DOWN 1 -#define PI_PUD_UP 2 +#define PI_PUD_OFF 0 +#define PI_PUD_DOWN 1 +#define PI_PUD_UP 2 @@ -406,14 +417,14 @@ int gpioRead (unsigned gpio); /* level: 0-1 */ -#define PI_OFF 0 -#define PI_ON 1 +#define PI_OFF 0 +#define PI_ON 1 -#define PI_CLEAR 0 -#define PI_SET 1 +#define PI_CLEAR 0 +#define PI_SET 1 -#define PI_LOW 0 -#define PI_HIGH 1 +#define PI_LOW 0 +#define PI_HIGH 1 /* level: only reported for gpio timeout, see gpioSetWatchdogTimeout */ @@ -1255,6 +1266,68 @@ int gpioSetTimerFuncEx(unsigned timer, +/* ----------------------------------------------------------------------- */ +pthread_t *gpioStartThread(ThreadFunc_t func, void *arg); +/*-------------------------------------------------------------------------*/ +/* Starts a new thread of execution with func as the main routine. + + Returns a pointer to pthread_t if OK, otherwise NULL. + + The function is passed the single argument arg. + + The thread can be cancelled by passing the pointer to pthread_t to + gpioStopThread(). + + EXAMPLE: + + #include + #include + + void *myfunc(void *arg) + { + while (1) + { + printf("%s\n", arg); + sleep(1); + } + } + + int main(int argc, char *argv[]) + { + pthread_t *p1, *p2, *p3; + + if (gpioInitialise() < 0) return 1; + + p1 = gpioStartThread(myfunc, "thread 1"); sleep(3); + + p2 = gpioStartThread(myfunc, "thread 2"); sleep(3); + + p3 = gpioStartThread(myfunc, "thread 3"); sleep(3); + + gpioStopThread(p3); sleep(3); + + gpioStopThread(p2); sleep(3); + + gpioStopThread(p1); sleep(3); + + gpioTerminate(); + } +*/ + + + +/* ----------------------------------------------------------------------- */ +void gpioStopThread(pthread_t *pth); +/*-------------------------------------------------------------------------*/ +/* Cancels the thread pointed at by pth. + + No value is returned. + + The thread to be stopped should have been started with gpioStartThread(). +*/ + + + /*-------------------------------------------------------------------------*/ int gpioSetSignalFunc(unsigned signum, gpioSignalFunc_t f); @@ -1518,9 +1591,17 @@ unsigned gpioHardwareRevision(void); EXAMPLES: - for "Revision : 0002" the function returns 2. - for "Revision : 000f" the function returns 15. - for "Revision : 000g" the function returns 0. + for "Revision : 0002" the function returns 2. + for "Revision : 000f" the function returns 15. + for "Revision : 000g" the function returns 0. +*/ + + + +/*-------------------------------------------------------------------------*/ +unsigned gpioVersion(void); +/*-------------------------------------------------------------------------*/ +/* Returns the pigpio version. */ @@ -1734,6 +1815,7 @@ void gpioWaveDump(void); #define PI_CMD_PFG 23 #define PI_CMD_PRRG 24 #define PI_CMD_HELP 25 +#define PI_CMD_PIGPV 26 /* The following command only works on the socket interface. @@ -1792,8 +1874,8 @@ after this command is issued. #define PI_TOO_MANY_PULSES -36 /* waveform has too many pulses */ #define PI_TOO_MANY_CHARS -37 /* waveform has too many chars */ #define PI_NOT_SERIAL_GPIO -38 /* no serial read in progress on gpio */ -#define PI_BAD_SERIAL_STRUC -39 /* bad null serial structure parameter */ -#define PI_BAD_SERIAL_BUF -40 /* bad null serial buf parameter */ +#define PI_BAD_SERIAL_STRUC -39 /* bad (null) serial structure parameter */ +#define PI_BAD_SERIAL_BUF -40 /* bad (null) serial buf parameter */ #define PI_NOT_PERMITTED -41 /* gpio operation not permitted */ #define PI_SOME_PERMITTED -42 /* one or more gpios not permitted */ @@ -1816,3 +1898,4 @@ after this command is issued. #define PI_DEFAULT_UPDATE_MASK_R2 0xFBC6CF9C #endif + diff --git a/pigpio.py b/pigpio.py index d4e7894..c9e4be5 100644 --- a/pigpio.py +++ b/pigpio.py @@ -76,7 +76,7 @@ import threading import os import atexit -VERSION = "1.0" +VERSION = "1.1" # gpio levels @@ -92,9 +92,9 @@ TIMEOUT = 2 # gpio edges -EITHER_EDGE = 0 -RISING_EDGE = 1 -FALLING_EDGE = 2 +RISING_EDGE = 0 +FALLING_EDGE = 1 +EITHER_EDGE = 2 # gpio modes @@ -338,12 +338,14 @@ class _callback_thread(threading.Thread): def __init__(self): """Initialises notifications.""" threading.Thread.__init__(self) + self.go = False self.daemon = True self.monitor = 0 self.callbacks = [] self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((_host,_port)) self.handle = _pigpio_command(self.sock, _PI_CMD_NOIB, 0, 0) + self.go = True self.start() def stop(self): @@ -376,13 +378,23 @@ class _callback_thread(threading.Thread): notify_begin(self.handle, self.monitor) def run(self): + """Execute the notification thread.""" - self.go = True + lastLevel = 0 + + MSG_SIZ = 12 + while self.go: - seq_no, flags, tick, level = ( - struct.unpack('HHII', self.sock.recv(12, socket.MSG_WAITALL))) + + buf = self.sock.recv(MSG_SIZ) + + while self.go and len(buf) < MSG_SIZ: + buf += self.sock.recv(MSG_SIZ-len(buf)) + if self.go: + seq, flags, tick, level = (struct.unpack('HHII', buf)) + if flags == 0: changed = level ^ lastLevel lastLevel = level @@ -391,19 +403,18 @@ class _callback_thread(threading.Thread): newLevel = 0 if cb.bit & level: newLevel = 1 - if (cb.edge == EITHER_EDGE or - cb.edge == RISING_EDGE and newLevel == 1 or - cb.edge == FALLING_EDGE and newLevel == 0): + if (cb.edge ^ newLevel): cb.func(cb.gpio, newLevel, tick) else: gpio = flags & 31 for cb in self.callbacks: if cb.gpio == gpio: cb.func(cb.gpio, TIMEOUT, tick) - + self.sock.close() class _wait_for_edge: + """A class to encapsulate waiting for gpio edges.""" def __init__(self, gpio, edge, timeout): @@ -1553,20 +1564,18 @@ def start(host = os.getenv("PIGPIO_ADDR", ''), else: h = _host errStr = "Can't connect to pigpio on " + str(h) + "(" + str(_port) + ")" - print("********************************************************") + print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") print(errStr) print("") - print("Did you start the pigpio daemon?") - print("(sudo pigpiod)") + print("Did you start the pigpio daemon? E.g. sudo pigpiod") print("") print("Did you specify the correct Pi host/port in the environment") print("variables PIGPIO_ADDR/PIGPIO_PORT?") - print("(e.g. export PIGPIO_ADDR=soft, export PIGPIO_PORT=8888)") + print("E.g. export PIGPIO_ADDR=soft, export PIGPIO_PORT=8888") print("") print("Did you specify the correct Pi host/port in the") - print("pigpio.start() function") - print("(e.g. pigpio.start('soft', 8888))") - print("********************************************************") + print("pigpio.start() function? E.g. pigpio.start('soft', 8888))") + print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") raise def stop(): diff --git a/pigpiod_if.c b/pigpiod_if.c new file mode 100644 index 0000000..6f095cb --- /dev/null +++ b/pigpiod_if.c @@ -0,0 +1,688 @@ +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to +*/ + +/* PIGPIOD_IF_VERSION 1 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "pigpio.h" +#include "command.h" + +#include "pigpiod_if.h" + +#define PISCOPE_MAX_REPORTS_PER_READ 4096 + +#define STACK_SIZE (256*1024) + +typedef void (*CBF_t) (); + +struct callback_s +{ + int id; + int gpio; + int edge; + CBF_t f; + void * user; + int ex; + callback_t *prev; + callback_t *next; +}; + +/* GLOBALS ---------------------------------------------------------------- */ + +static gpioReport_t gReport[PISCOPE_MAX_REPORTS_PER_READ]; + +static int gPigCommand = -1; +static int gPigHandle = -1; +static int gPigNotify = -1; + +static uint32_t gNotifyBits; + +callback_t *gCallBackFirst = 0; +callback_t *gCallBackLast = 0; + +static int gPigStarted = 0; + +static pthread_t *pthNotify; + +/* PRIVATE ---------------------------------------------------------------- */ + +static int pigpio_command(int fd, int command, int p1, int p2) +{ + cmdCmd_t cmd; + + cmd.cmd = command; + cmd.p1 = p1; + cmd.p2 = p2; + cmd.res = 0; + + if (send(fd, &cmd, sizeof(cmdCmd_t), 0) != sizeof(cmdCmd_t)) + return pigif_bad_send; + + if (recv(fd, &cmd, sizeof(cmdCmd_t), MSG_WAITALL) != sizeof(cmdCmd_t)) + return pigif_bad_recv; + + return cmd.res; +} + +static int pigpioOpenSocket(char *addr, char *port) +{ + int sock, err; + struct addrinfo hints, *res, *rp; + const char *addrStr, *portStr; + + if (!addr) + { + addrStr = getenv(PI_ENVADDR); + + if ((!addrStr) || (!strlen(addrStr))) + { + addrStr = PI_DEFAULT_SOCKET_ADDR_STR; + } + } + else addrStr = addr; + + if (!port) + { + portStr = getenv(PI_ENVPORT); + + if ((!portStr) || (!strlen(portStr))) + { + portStr = PI_DEFAULT_SOCKET_PORT_STR; + } + } + else portStr = port; + + memset (&hints, 0, sizeof (hints)); + + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags |= AI_CANONNAME; + + err = getaddrinfo (addrStr, portStr, &hints, &res); + + if (err) return pigif_bad_getaddrinfo; + + for (rp=res; rp!=NULL; rp=rp->ai_next) + { + sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + + if (sock == -1) continue; + + if (connect(sock, rp->ai_addr, rp->ai_addrlen) != -1) break; + } + + freeaddrinfo(res); + + if (rp == NULL) return pigif_bad_connect; + + return sock; +} + +static void dispatch_notification(gpioReport_t *r) +{ + static uint32_t lastLevel = 0; + + callback_t *p; + uint32_t changed; + int l, g; + + /* + printf("s=%d f=%d l=%8X, t=%10u\n", + r->seqno, r->flags, r->level, r->tick); + */ + + if (r->flags == 0) + { + changed = (r->level ^ lastLevel) & gNotifyBits; + + lastLevel = r->level; + + p = gCallBackFirst; + + while (p) + { + if (changed & (1<<(p->gpio))) + { + if ((r->level) & (1<<(p->gpio))) l = 1; else l = 0; + if ((p->edge) ^ l) + { + if (p->ex) (p->f)(p->gpio, l, r->tick, p->user); + else (p->f)(p->gpio, l, r->tick); + } + } + p = p->next; + } + } + else + { + g = (r->flags) & 31; + + p = gCallBackFirst; + + while (p) + { + if ((p->gpio) == g) + { + if (p->ex) (p->f)(g, PI_TIMEOUT, r->tick, p->user); + else (p->f)(g, PI_TIMEOUT, r->tick); + } + p = p->next; + } + } +} + +static void *pthNotifyThread(void *x) +{ + static int got = 0; + + int bytes, r; + + while (1) + { + bytes = read(gPigNotify, (char*)&gReport+got, sizeof(gReport)-got); + + if (bytes > 0) got += bytes; + else break; + + r = 0; + + while (got >= sizeof(gpioReport_t)) + { + dispatch_notification(&gReport[r]); + + r++; + + got -= sizeof(gpioReport_t); + } + + /* copy any partial report to start of array */ + + if (got && r) gReport[0] = gReport[r]; + } + return 0; +} + +static void findNotifyBits(void) +{ + callback_t *p; + uint32_t bits = 0; + + p = gCallBackFirst; + + while (p) + { + bits |= (1<<(p->gpio)); + p = p->next; + } + + if (bits != gNotifyBits) + { + gNotifyBits = bits; + pigpio_command(gPigCommand, PI_CMD_NB, gPigHandle, gNotifyBits); + } +} + +static void _wfe(int gpio, int level, uint32_t tick, void *user) +{ + *(int *)user = 1; +} + +static int intCallback(int gpio, int edge, void *f, void *user, int ex) +{ + static int id = 0; + callback_t *p; + + if ((gpio >=0) && (gpio < 32) && (edge >=0) && (edge <= 2) && f) + { + /* prevent duplicates */ + + p = gCallBackFirst; + + while (p) + { + if ((p->gpio == gpio) && (p->edge == edge) && (p->f == f)) + { + return pigif_duplicate_callback; + } + p = p->next; + } + + p = malloc(sizeof(callback_t)); + + if (p) + { + if (!gCallBackFirst) gCallBackFirst = p; + + p->id = id++; + p->gpio = gpio; + p->edge = edge; + p->f = f; + p->user = user; + p->ex = ex; + p->next = 0; + p->prev = gCallBackLast; + + if (p->prev) (p->prev)->next = p; + gCallBackLast = p; + + findNotifyBits(); + + return p->id; + } + + return pigif_bad_malloc; + } + + return pigif_bad_callback; +} + +/* PUBLIC ----------------------------------------------------------------- */ + +double time_time(void) +{ + struct timeval tv; + double t; + + gettimeofday(&tv, 0); + + t = (double)tv.tv_sec + ((double)tv.tv_usec / 1E6); + + return t; +} + +void time_sleep(double seconds) +{ + struct timespec ts, rem; + + if (seconds > 0.0) + { + ts.tv_sec = seconds; + ts.tv_nsec = (seconds-(double)ts.tv_sec) * 1E9; + + while (clock_nanosleep(CLOCK_REALTIME, 0, &ts, &rem)) + { + /* copy remaining time to ts */ + ts.tv_sec = rem.tv_sec; + ts.tv_nsec = rem.tv_nsec; + } + } +} + +const char *pigpio_error(int error) +{ + if (error > -1000) return cmdErrStr(error); + else + { + switch(error) + { + case pigif_bad_send: + return "failed to send to pigpiod"; + case pigif_bad_recv: + return "failed to receive from pigpiod"; + case pigif_bad_getaddrinfo: + return "failed to find address of pigpiod"; + case pigif_bad_connect: + return "failed to connect to pigpiod"; + case pigif_bad_socket: + return "failed to create socket"; + case pigif_bad_noib: + return "failed to open noib"; + case pigif_duplicate_callback: + return "identical callback exists"; + case pigif_bad_malloc: + return "failed to malloc"; + case pigif_bad_callback: + return "bad callback parameter"; + case pigif_notify_failed: + return "failed to create notification thread"; + case pigif_callback_not_found: + return "callback not found"; + default: + return "unknown error"; + } + } +} + +unsigned pigpiod_if_version(void) +{ + return PIGPIOD_IF_VERSION; +} + +pthread_t *start_thread(ThreadFunc_t func, void *arg) +{ + pthread_t *pth; + pthread_attr_t pthAttr; + + pth = malloc(sizeof(pthread_t)); + + if (pth) + { + if (pthread_attr_init(&pthAttr)) + { + perror("pthread_attr_init failed"); + free(pth); + return NULL; + } + + if (pthread_attr_setstacksize(&pthAttr, STACK_SIZE)) + { + perror("pthread_attr_setstacksize failed"); + free(pth); + return NULL; + } + + if (pthread_create(pth, &pthAttr, func, arg)) + { + perror("pthread_create socket failed"); + free(pth); + return NULL; + } + } + return pth; +} + +void stop_thread(pthread_t *pth) +{ + if (pth) + { + pthread_cancel(*pth); + pthread_join(*pth, NULL); + } +} + +int pigpio_start(char *addrStr, char *portStr) +{ + if (!gPigStarted) + { + gPigCommand = pigpioOpenSocket(addrStr, portStr); + + if (gPigCommand >= 0) + { + gPigNotify = pigpioOpenSocket(addrStr, portStr); + + if (gPigNotify >= 0) + { + gPigHandle = pigpio_command(gPigNotify, PI_CMD_NOIB, 0, 0); + + if (gPigHandle < 0) return pigif_bad_noib; + else + { + pthNotify = start_thread(pthNotifyThread, 0); + if (pthNotify) + { + gPigStarted = 1; + return 0; + } + else return pigif_notify_failed; + } + } + else return gPigNotify; + } + else return gPigCommand; + } + return 0; +} + +void pigpio_stop(void) +{ + gPigStarted = 0; + + if (pthNotify) + { + stop_thread(pthNotify); + pthNotify = 0; + } + + if (gPigNotify >= 0) + { + if (gPigHandle >= 0) + { + pigpio_command(gPigNotify, PI_CMD_NC, gPigHandle, 0); + gPigHandle = -1; + } + + close(gPigNotify); + gPigNotify = -1; + } + + if (gPigCommand >= 0) + { + if (gPigHandle >= 0) + { + pigpio_command(gPigCommand, PI_CMD_NC, gPigHandle, 0); + gPigHandle = -1; + } + + close(gPigCommand); + gPigCommand = -1; + } +} + +int set_mode(int gpio, int mode) +{ + return pigpio_command(gPigCommand, PI_CMD_MODES, gpio, mode); +} + +int get_mode(int gpio) +{ + return pigpio_command(gPigCommand, PI_CMD_MODEG, gpio, 0); +} + +int set_pull_up_down(int gpio, int pud) +{ + return pigpio_command(gPigCommand, PI_CMD_PUD, gpio, pud); +} + +int read_gpio(int gpio) +{ + return pigpio_command(gPigCommand, PI_CMD_READ, gpio, 0); +} + +int write_gpio(int gpio, int level) +{ + return pigpio_command(gPigCommand, PI_CMD_WRITE, gpio, level); +} + +int set_PWM_dutycycle(int user_gpio, int dutycycle) +{ + return pigpio_command(gPigCommand, PI_CMD_PWM, user_gpio, dutycycle); +} + +int set_PWM_range(int user_gpio, int range_) +{ + return pigpio_command(gPigCommand, PI_CMD_PRS, user_gpio, range_); +} + +int get_PWM_range(int user_gpio) +{ + return pigpio_command(gPigCommand, PI_CMD_PRG, user_gpio, 0); +} + +int get_PWM_real_range(int user_gpio) +{ + return pigpio_command(gPigCommand, PI_CMD_PRRG, user_gpio, 0); +} + +int set_PWM_frequency(int user_gpio, int frequency) +{ + return pigpio_command(gPigCommand, PI_CMD_PFS, user_gpio, frequency); +} + +int get_PWM_frequency(int user_gpio) +{ + return pigpio_command(gPigCommand, PI_CMD_PFG, user_gpio, 0); +} + +int set_servo_pulsewidth(int user_gpio, int pulsewidth) +{ + return pigpio_command(gPigCommand, PI_CMD_SERVO, user_gpio, pulsewidth); +} + +int notify_open(void) +{ + return pigpio_command(gPigCommand, PI_CMD_NO, 0, 0); +} + +int notify_begin(int handle, uint32_t bits) +{ + return pigpio_command(gPigCommand, PI_CMD_NB, handle, bits); +} + +int notify_pause(int handle) +{ + return pigpio_command(gPigCommand, PI_CMD_NB, handle, 0); +} + +int notify_close(int handle) +{ + return pigpio_command(gPigCommand, PI_CMD_NC, handle, 0); +} + +int set_watchdog(int user_gpio, int timeout) +{ + return pigpio_command(gPigCommand, PI_CMD_WDOG, user_gpio, timeout); +} + +uint32_t read_bank_1(void) +{ + return pigpio_command(gPigCommand, PI_CMD_BR1, 0, 0); +} + +uint32_t read_bank_2(void) +{ + return pigpio_command(gPigCommand, PI_CMD_BR2, 0, 0); +} + +int clear_bank_1(uint32_t levels) +{ + return pigpio_command(gPigCommand, PI_CMD_BC1, levels, 0); +} + +int clear_bank_2(uint32_t levels) +{ + return pigpio_command(gPigCommand, PI_CMD_BC2, levels, 0); +} + +int set_bank_1(uint32_t levels) +{ + return pigpio_command(gPigCommand, PI_CMD_BS1, levels, 0); +} + +int set_bank_2(uint32_t levels) +{ + return pigpio_command(gPigCommand, PI_CMD_BS2, levels, 0); +} + +uint32_t get_current_tick(void) +{ + return pigpio_command(gPigCommand, PI_CMD_TICK, 0, 0); +} + +uint32_t get_hardware_revision(void) +{ + return pigpio_command(gPigCommand, PI_CMD_HWVER, 0, 0); +} + +unsigned get_pigpio_version(void) +{ + return pigpio_command(gPigCommand, PI_CMD_PIGPV, 0, 0); +} + +int callback(int gpio, int edge, CBFunc_t f) +{ + return intCallback(gpio, edge, f, 0, 0); +} + +int callback_ex(int gpio, int edge, CBFuncEx_t f, void *user) +{ + return intCallback(gpio, edge, f, user, 1); +} + +int callback_cancel(int id) +{ + callback_t *p; + + p = gCallBackFirst; + + while (p) + { + if (p->id == id) + { + if (p->prev) p->prev->next = p->next; + else gCallBackFirst = p->next; + + if (p->next) p->next->prev = p->prev; + else gCallBackLast = p->prev; + + free(p); + + findNotifyBits(); + + return 0; + } + p = p->next; + } + return pigif_callback_not_found; +} + +int wait_for_edge(int gpio, int edge, double timeout) +{ + int triggered = 0; + int id; + double due; + + if (timeout <= 0.0) return 0; + + due = time_time() + timeout; + + id = callback_ex(gpio, edge, _wfe, &triggered); + + while (!triggered && (time_time() < due)) time_sleep(0.1); + + callback_cancel(id); + + return triggered; +} + diff --git a/pigpiod_if.h b/pigpiod_if.h new file mode 100644 index 0000000..bde8a71 --- /dev/null +++ b/pigpiod_if.h @@ -0,0 +1,527 @@ +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to +*/ + +#ifndef PIGPIOD_IF_H +#define PIGPIOD_IF_H + +#include "pigpio.h" + +#define PIGPIOD_IF_VERSION 1 + +typedef enum +{ + pigif_bad_send = -2000, + pigif_bad_recv = -2001, + pigif_bad_getaddrinfo = -2002, + pigif_bad_connect = -2003, + pigif_bad_socket = -2004, + pigif_bad_noib = -2005, + pigif_duplicate_callback = -2006, + pigif_bad_malloc = -2007, + pigif_bad_callback = -2008, + pigif_notify_failed = -2009, + pigif_callback_not_found = -2010, +} piscopeError_t; + + +typedef void (*CBFunc_t) (int gpio, int level, uint32_t tick); + +typedef void (*CBFuncEx_t)(int gpio, int level, uint32_t tick, void * user); + +typedef struct callback_s callback_t; + +#define RISING_EDGE 0 +#define FALLING_EDGE 1 +#define EITHER_EDGE 2 + +double time_time(void); +/* Return the current time in seconds since the Epoch.*/ + +void time_sleep(double seconds); +/* Delay execution for a given number of seconds */ + +const char *pigpio_error(int error); +/* Return a string for a pigpio library error. */ + +unsigned pigpiod_if_version(void); +/* Return the pigpiod_if version. */ + +pthread_t *start_thread(ThreadFunc_t func, void *arg); +/* Starts a new thread of execution with func as the main routine. + + Returns a pointer to pthread_t if OK, otherwise NULL. + + The function is passed the single argument arg. + + The thread can be cancelled by passing the pointer to pthread_t to + gpioStopThread(). +*/ + +void stop_thread(pthread_t *pth); +/* Cancels the thread pointed at by pth. + + No value is returned. + + The thread to be stopped should have been started with gpioStartThread(). +*/ + +int pigpio_start(char *addrStr, char *portStr); +/* Connect to the pigpio daemon. Reserving command and + notification streams. + + addrStr specifies the host or IP address of the Pi running + the pigpio daemon. It may be NULL in which case localhost + is used unless overriden by the PIGPIO_ADDR environment + variable. + + portStr specifies the port address used by the Pi running + the pigpio daemon. It may be NULL in which case "8888" + is used unless overriden by the PIGPIO_PORT environment + variable. +*/ + +void pigpio_stop(void); +/* + Terminates the connection to the pigpio daemon and releases + resources used by the library. +*/ + +int set_mode(int gpio, int mode); +/* Set the gpio mode. + + gpio: 0-53. + mode: INPUT, OUTPUT, ALT0, ALT1, ALT2, ALT3, ALT4, ALT5. + + Returns 0 if OK, otherwise PI_BAD_GPIO, PI_BAD_MODE, + or PI_NOT_PERMITTED. +*/ + +int get_mode(int gpio); +/* Get the gpio mode. + + Returns the gpio mode if OK, otherwise PI_BAD_GPIO. + + gpio: 0-53. +*/ + +int set_pull_up_down(int gpio, int pud); +/* Set or clear the gpio pull-up/down resistor. + + Returns 0 if OK, otherwise PI_BAD_GPIO, PI_BAD_PUD, + or PI_NOT_PERMITTED. + + gpio: 0-53. + pud: PUD_UP, PUD_DOWN, PUD_OFF. +*/ + +int read_gpio(int gpio); +/* Read the gpio level. + + Returns the gpio level if OK, otherwise PI_BAD_GPIO. + + gpio:0-53. +*/ + +int write_gpio(int gpio, int level); +/* + Write the gpio level. + + Returns 0 if OK, otherwise PI_BAD_GPIO, PI_BAD_LEVEL, + or PI_NOT_PERMITTED. + + gpio: 0-53. + level: 0, 1. + + Notes + + If PWM or servo pulses are active on the gpio they are switched off. +*/ + +int set_PWM_dutycycle(int user_gpio, int dutycycle); +/* Start (non-zero dutycycle) or stop (0) PWM pulses on the gpio. + + Returns 0 if OK, otherwise PI_BAD_USER_GPIO, PI_BAD_DUTYCYCLE, + or PI_NOT_PERMITTED. + + user_gpio: 0-31. + dutycycle: 0-range (range defaults to 255). + + Notes + + The set_PWM_range() function can change the default range of 255. +*/ + +int set_PWM_range(int user_gpio, int range_); +/* Set the range of PWM values to be used on the gpio. + + Returns 0 if OK, otherwise PI_BAD_USER_GPIO, PI_BAD_DUTYRANGE, + or PI_NOT_PERMITTED. + + user_gpio: 0-31. + range_: 25-40000. + + Notes + + If PWM is currently active on the gpio its dutycycle will be + scaled to reflect the new range. + + The real range, the number of steps between fully off and fully on + for each of the 18 available gpio frequencies is + + 25(#1), 50(#2), 100(#3), 125(#4), 200(#5), 250(#6), 400(#7), + 500(#8), 625(#9), 800(#10), 1000(#11), 1250(#12), 2000(#13), + 2500(#14), 4000(#15), 5000(#16), 10000(#17), 20000(#18) + + The real value set by set_PWM_range is + (dutycycle * real range) / range. + +*/ + +int get_PWM_range(int user_gpio); +/* Get the range of PWM values being used on the gpio. + + Returns the dutycycle range used for the gpio if OK, + otherwise PI_BAD_USER_GPIO. + + user_gpio: 0-31. +*/ + +int get_PWM_real_range(int user_gpio); +/* Get the real underlying range of PWM values being used on the gpio. + + Returns the real range used for the gpio if OK, + otherwise PI_BAD_USER_GPIO. + + user_gpio: 0-31. +*/ + +int set_PWM_frequency(int user_gpio, int frequency); +/* + Set the frequency (in Hz) of the PWM to be used on the gpio. + + Returns the numerically closest frequency if OK, otherwise + PI_BAD_USER_GPIO or PI_NOT_PERMITTED. + + user_gpio: 0-31. + frequency: 0- (Hz). + + The selectable frequencies depend upon the sample rate which + may be 1, 2, 4, 5, 8, or 10 microseconds (default 5). The + sample rate is set when the C pigpio library is started. + + Each gpio can be independently set to one of 18 different + PWM frequencies. + + If PWM is currently active on the gpio it will be switched + off and then back on at the new frequency. + + 1us 40000, 20000, 10000, 8000, 5000, 4000, 2500, 2000, 1600, + 1250, 1000, 800, 500, 400, 250, 200, 100, 50 + + 2us 20000, 10000, 5000, 4000, 2500, 2000, 1250, 1000, 800, + 625, 500, 400, 250, 200, 125, 100, 50, 25 + + 4us 10000, 5000, 2500, 2000, 1250, 1000, 625, 500, 400, + 313, 250, 200, 125, 100, 63, 50, 25, 13 + + 5us 8000, 4000, 2000, 1600, 1000, 800, 500, 400, 320, + 250, 200, 160, 100, 80, 50, 40, 20, 10 + + 8us 5000, 2500, 1250, 1000, 625, 500, 313, 250, 200, + 156, 125, 100, 63, 50, 31, 25, 13, 6 + + 10us 4000, 2000, 1000, 800, 500, 400, 250, 200, 160, + 125, 100, 80, 50, 40, 25, 20, 10, 5 +*/ + +int get_PWM_frequency(int user_gpio); +/* + Get the frequency of PWM being used on the gpio. + + Returns the frequency (in hertz) used for the gpio if OK, + otherwise PI_BAD_USER_GPIO. + + user_gpio: 0-31. +*/ + +int set_servo_pulsewidth(int user_gpio, int pulsewidth); +/* + Start (500-2500) or stop (0) servo pulses on the gpio. + + Returns 0 if OK, otherwise PI_BAD_USER_GPIO, PI_BAD_PULSEWIDTH or + PI_NOT_PERMITTED. + + user_gpio: 0-31. + pulsewidth: 0 (off), 500 (most anti-clockwise) - 2500 (most clockwise). + + The selected pulsewidth will continue to be transmitted until + changed by a subsequent call to set_servo_pulsewidth(). + + The pulsewidths supported by servos varies and should probably be + determined by experiment. A value of 1500 should always be safe and + represents the mid-point of rotation. + + You can DAMAGE a servo if you command it to move beyond its limits. + + OTHER UPDATE RATES: + + This function updates servos at 50Hz. If you wish to use a different + update frequency you will have to use the PWM functions. + + Update Rate (Hz) 50 100 200 400 500 + 1E6/Hz 20000 10000 5000 2500 2000 + + Firstly set the desired PWM frequency using set_PWM_frequency(). + + Then set the PWM range using set_PWM_range() to 1E6/Hz. + Doing this allows you to use units of microseconds when setting + the servo pulse width. + + E.g. If you want to update a servo connected to gpio 25 at 400Hz + + set_PWM_frequency(25, 400); + set_PWM_range(25, 2500); + + Thereafter use the set_PWM_dutycycle() function to move the servo, + e.g. set_PWM_dutycycle(25, 1500) will set a 1500 us pulse. +*/ + +int notify_open(void); +/* + Get a free notification handle. + + Returns a handle greater than or equal to zero if OK, + otherwise PI_NO_HANDLE. + + A notification is a method for being notified of gpio state + changes via a pipe. + + Pipes are only accessible from the local machine so this function + serves no purpose if you are using the library from a remote machine. + The in-built (socket) notifications provided by callback() + should be used instead. + + Notifications for handle x will be available at the pipe + named /dev/pigpiox (where x is the handle number). + E.g. if the function returns 15 then the notifications must be + read from /dev/pigpio15. +*/ + +int notify_begin(int handle, uint32_t bits); +/* + Start notifications on a previously opened handle. + + Returns 0 if OK, otherwise PI_BAD_HANDLE. + + handle: 0-31 (as returned by notify_open()) + bits: a mask indicating the gpios to be notified. + + The notification sends state changes for each gpio whose + corresponding bit in bits is set. + + Notes + + Each notification occupies 12 bytes in the fifo as follows: + + H (16 bit) seqno + H (16 bit) flags + I (32 bit) tick + I (32 bit) level +*/ + +int notify_pause(int handle); +/* + Pause notifications on a previously opened handle. + + Returns 0 if OK, otherwise PI_BAD_HANDLE. + + handle: 0-31 (as returned by notify_open()) + + Notifications for the handle are suspended until + notify_begin() is called again. +*/ + +int notify_close(int handle); +/* + Stop notifications on a previously opened handle and + release the handle for reuse. + + Returns 0 if OK, otherwise PI_BAD_HANDLE. + + handle: 0-31 (as returned by notify_open()) +*/ + +int set_watchdog(int user_gpio, int timeout); +/* + Sets a watchdog for a gpio. + + Returns 0 if OK, otherwise PI_BAD_USER_GPIO + or PI_BAD_WDOG_TIMEOUT. + + user_gpio: 0-31. + timeout: 0-60000. + + The watchdog is nominally in milliseconds. + + Only one watchdog may be registered per gpio. + + The watchdog may be cancelled by setting timeout to 0. + + If no level change has been detected for the gpio for timeout + milliseconds any notification for the gpio has a report written + to the fifo with the flags set to indicate a watchdog timeout. + + The callback() and callback_ex functions interpret the flags and will + call registered callbacks for the gpio with level TIMEOUT. +*/ + +uint32_t read_bank_1(void); +/* + Read the levels of the bank 1 gpios (gpios 0-31). + + The returned 32 bit integer has a bit set if the corresponding + gpio is logic 1. Gpio n has bit value (1<