diff --git a/Makefile b/Makefile index 2b69494..8684ac6 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,9 @@ SIZE = size CFLAGS = -O3 -Wall -all: libpigpio.a checklib demolib pig2vcd pigpiod pigs +ALL = libpigpio.a checklib demolib pig2vcd pigpiod pigs + +all: $(ALL) checklib: checklib.o libpigpio.a $(CC) -o checklib checklib.c -L. -lpigpio -lpthread -lrt @@ -19,31 +21,29 @@ pig2vcd: pig2vcd.o pigpiod: pigpiod.o libpigpio.a $(CC) -o pigpiod pigpiod.c -L. -lpigpio -lpthread -lrt -pigs: pigs.o command.o +pigs: command.o $(CC) -o pigs pigs.c command.c -.c.o: - $(CC) -c $(CFLAGS) $< - clean: - rm -f *.o *.i *.s *~ libpigpio.a checklib demolib pigpiod pigs pig2vcd + rm -f *.o *.i *.s *~ $(ALL) install: $(LIB) - sudo install -m 0755 -d /usr/local/bin 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 0644 pigpio.h /usr/local/include - sudo install -m 0644 libpigpio.a /usr/local/lib + sudo python setup.py install uninstall: + sudo rm -f /usr/local/include/pigpio.h + sudo rm -f /usr/local/lib/libpigpio.a sudo rm -f /usr/local/bin/pig2vcd sudo rm -f /usr/local/bin/pigpiod sudo rm -f /usr/local/bin/pigs - sudo rm -f /usr/local/include/pigpio.h - sudo rm -f /usr/local/lib/libpigpio.a LIB = libpigpio.a OBJ = pigpio.o command.o @@ -53,11 +53,13 @@ $(LIB): $(OBJ) $(RANLIB) $(LIB) $(SIZE) $(LIB) +# generated using gcc -M *.c -# DO NOT DELETE +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 +pigs.o: pigs.c pigpio.h command.h -checklib.o: checklib.c pigpio.h -demolib.o: demolib.c pigpio.h -pig2vcd: pigpio.h -pigpiod: pigpiod.c pigpio.h -pigs: pigs.c command.c pigpio.h command.h diff --git a/README b/README index 41c00cb..b673035 100644 --- a/README +++ b/README @@ -15,6 +15,7 @@ This will install: 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 TEST @@ -22,8 +23,8 @@ To test the library do sudo ./checklib -checklib.c, demolib.c, pig2vcd.c, pigpiod.c, and pigs.c show examples -of interfacing with the library. +checklib.c, demolib.c, pig2vcd.c, pigpiod.c, pigs.c, and pigpio.py +show examples of interfacing with the library. DAEMON @@ -59,9 +60,30 @@ cat /dev/pigerr & echo "help" >/dev/pigpio +PYTHON INTERFACE + +If the pigpiod daemon is running you can test the Python +interface by entering the following commands. + +python + +import pigpio + +pigpio.start() + +print(pigpio.get_current_tick()) + +print(hex(pigpio.read_bank_1())) + +pigpio.stop() + +help(pigpio) + +quit() + STOP DAEMON -To stop the daemon +To stop the pigpiod daemon sudo killall pigpiod diff --git a/command.c b/command.c index ab46aa8..c3eba85 100644 --- a/command.c +++ b/command.c @@ -26,7 +26,7 @@ For more information, please refer to */ /* -This version is for pigpio version 4+ +This version is for pigpio version 7+ */ #include @@ -141,7 +141,7 @@ static errInfo_t errInfo[]= {PI_BAD_CLK_SOURCE , "clock source not 0-1"}, {PI_BAD_CLK_MICROS , "clock micros not 1, 2, 4, 5, 8, or 10"}, {PI_BAD_BUF_MILLIS , "buf millis not 100-10000"}, - {PI_BAD_DUTY_RANGE , "dutycycle range not 25-40000"}, + {PI_BAD_DUTYRANGE , "dutycycle range not 25-40000"}, {PI_BAD_SIGNUM , "signum not 0-63"}, {PI_BAD_PATHNAME , "can't open pathname"}, {PI_NO_HANDLE , "no handle available"}, @@ -159,7 +159,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_NOT_PERMITTED , "no permission to update gpio"}, + {PI_SOME_PERMITTED , "no permission to update one or more gpios"}, }; static char * fmtMdeStr="RW540123"; @@ -289,4 +290,3 @@ char * cmdErrStr(int error) } return "unknown error"; } - diff --git a/command.h b/command.h index 9d2da7d..6ace8c7 100644 --- a/command.h +++ b/command.h @@ -26,7 +26,7 @@ For more information, please refer to */ /* -This version is for pigpio version 3+ +This version is for pigpio version 7+ */ #ifndef COMMAND_H @@ -39,10 +39,10 @@ This version is for pigpio version 3+ typedef struct { - int cmd; - char * name; - int vt; - int rv; + int cmd; /* command number */ + char * name; /* command name */ + int vt; /* command verification type */ + int rv; /* command return value type */ } cmdInfo_t; extern cmdInfo_t cmdInfo[]; diff --git a/pigpio.c b/pigpio.c index c577ced..5b721b8 100644 --- a/pigpio.c +++ b/pigpio.c @@ -25,7 +25,7 @@ OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to */ -/* pigpio version 6 */ +/* pigpio version 7 */ #include #include @@ -229,6 +229,13 @@ bit 0 READ_LAST_NOT_SET_ERROR } \ while (0) +#define PERM_ERROR(format, arg...) \ + do \ + { \ + fprintf(stderr, "%s " format "\n", myTimeStamp(), ## arg); \ + } \ + while (0) + #define TIMER_ADD(a, b, result) \ do \ { \ @@ -519,8 +526,6 @@ bit 0 READ_LAST_NOT_SET_ERROR #define PI_WFRX_SERIAL 1 #define PI_WF_MICROS 2 -#define PI_WAVE_MAX_PULSES 3000 - #define DATUMS 2000 #define DEFAULT_PWM_IDX 5 @@ -704,12 +709,16 @@ static volatile gpioCfg_t gpioCfg = static volatile gpioStats_t gpioStats; +static int gpioMaskSet = 0; + /* initialise every gpioInitialise */ static struct timespec libStarted; /* initialse if not libInitialised */ +static uint64_t gpioMask; + static gpioPulse_t wf[3][PI_WAVE_MAX_PULSES]; static int wfc[3]={0, 0, 0}; @@ -732,7 +741,6 @@ static volatile uint32_t notifyBits = 0; static volatile int DMAstarted = 0; static int libInitialised = 0; -static unsigned hardwareRevision = 0; static int pthAlertRunning = 0; static int pthFifoRunning = 0; @@ -947,6 +955,7 @@ static uint32_t myGetTick(int pos) static void myDoCommand(cmdCmd_t * cmd) { int p1, p2, res; + uint32_t mask; p1 = cmd->p1; p2 = cmd->p2; @@ -956,7 +965,12 @@ static void myDoCommand(cmdCmd_t * cmd) switch (cmd->cmd) { case PI_CMD_MODES: - res = gpioSetMode(p1, p2); + if (gpioMask & (uint64_t)(1<>32; + + res = gpioWrite_Bits_32_53_Clear(p1&mask); + + if ((mask | p1) != mask) + { + PERM_ERROR("gpioWrite_Bits_32_53_Clear: bad levels %08X (permissions %08X)", + p1, mask); + res = PI_SOME_PERMITTED; + } break; case PI_CMD_BS1: - gpioWrite_Bits_0_31_Set(p1); + mask = gpioMask; + + res = gpioWrite_Bits_0_31_Set(p1&mask); + + if ((mask | p1) != mask) + { + PERM_ERROR("gpioWrite_Bits_0_31_Set: bad levels %08X (permissions %08X)", + p1, mask); + res = PI_SOME_PERMITTED; + } break; case PI_CMD_BS2: - gpioWrite_Bits_32_53_Set(p1); + mask = gpioMask>>32; + + res = gpioWrite_Bits_32_53_Set(p1&mask); + + if ((mask | p1) != mask) + { + PERM_ERROR("gpioWrite_Bits_32_53_Set: bad levels %08X (permissions %08X)", + p1, mask); + res = PI_SOME_PERMITTED; + } break; case PI_CMD_TICK: @@ -1274,7 +1354,7 @@ static void waveCbOPrint(int pos) p = waveCbVOadr(pos); - fprintf(stderr, "i=%lx s=%lx d=%lx len=%lx s=%lx nxt=%lx", + fprintf(stderr, "i=%lx s=%lx d=%lx len=%lx s=%lx nxt=%lx\n", p->info, p->src, p->dst, p->length, p->stride, p->next); } @@ -1669,7 +1749,7 @@ static void dmaCbPrint(int pos) p = dmaCB2adr(pos); - fprintf(stderr, "i=%lx s=%lx d=%lx len=%lx s=%lx nxt=%lx", + fprintf(stderr, "i=%lx s=%lx d=%lx len=%lx s=%lx nxt=%lx\n", p->info, p->src, p->dst, p->length, p->stride, p->next); } @@ -1962,13 +2042,15 @@ static void sigHandler(int signum) DBG(DBG_USER, "Debug level %d\n", gpioCfg.dbgLevel); } + else if (signum == SIGPIPE) + { + DBG(DBG_USER, "SIGPIPE received"); + } else { - /* close library safely and exit */ + /* exit */ - DBG(DBG_USER, "Unhandled signal %d, terminating\n", signum); - - gpioTerminate(); + DBG(DBG_MIN_LEVEL, "Unhandled signal %d, terminating\n", signum); exit(-1); } @@ -1976,11 +2058,9 @@ static void sigHandler(int signum) } else { - /* close library safely and exit */ + /* exit */ - DBG(DBG_USER, "Unhandled signal %d, terminating\n", signum); - - gpioTerminate(); + DBG(DBG_MIN_LEVEL, "Unhandled signal %d, terminating\n", signum); exit(-1); } @@ -2424,7 +2504,6 @@ static void * pthTimerTick(void *x) return 0; } - /* ----------------------------------------------------------------------- */ @@ -2465,6 +2544,7 @@ static void * pthFifoThread(void *x) break; case 1: + fprintf(outFifo, "%d\n", cmd.res); break; case 2: @@ -2860,7 +2940,7 @@ static int initDMAcbs(void) /* allocate memory for pointers to virtual and physical pages */ dmaBloc = mmap( - 0, (bufferBlocks+1)*sizeof(dmaPage_t *), + 0, (bufferBlocks+PI_WAVE_BLOCKS)*sizeof(dmaPage_t *), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_LOCKED, -1, 0); @@ -2869,7 +2949,7 @@ static int initDMAcbs(void) SOFT_ERROR(PI_INIT_FAILED, "mmap dma virtual failed (%m)"); dmaVirt = mmap( - 0, PAGES_PER_BLOCK*(bufferBlocks+1)*sizeof(dmaPage_t *), + 0, PAGES_PER_BLOCK*(bufferBlocks+PI_WAVE_BLOCKS)*sizeof(dmaPage_t *), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_LOCKED, -1, 0); @@ -2878,7 +2958,7 @@ static int initDMAcbs(void) SOFT_ERROR(PI_INIT_FAILED, "mmap dma virtual failed (%m)"); dmaPhys = mmap( - 0, PAGES_PER_BLOCK*(bufferBlocks+1)*sizeof(dmaPage_t *), + 0, PAGES_PER_BLOCK*(bufferBlocks+PI_WAVE_BLOCKS)*sizeof(dmaPage_t *), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_LOCKED, -1, 0); @@ -2901,7 +2981,7 @@ static int initDMAcbs(void) if (pagemapFd < 0) SOFT_ERROR(PI_INIT_FAILED, "open pagemap failed(%m)"); - for (i=0; i<(bufferBlocks+1); i++) initDMAblock(pagemapFd, i); + for (i=0; i<(bufferBlocks+PI_WAVE_BLOCKS); i++) initDMAblock(pagemapFd, i); close(pagemapFd); @@ -2918,7 +2998,10 @@ static int initDMAcbs(void) dmaInitCbs(); if (gpioCfg.dbgLevel >= DBG_DMACBS) + { + fprintf(stderr, "*** INPUT DMA CONTROL BLOCKS ***\n"); for (i=0; i PI_MAX_DUTYCYCLE_RANGE)) - SOFT_ERROR(PI_BAD_DUTY_RANGE, "gpio %d, bad range (%d)", gpio, range); + SOFT_ERROR(PI_BAD_DUTYRANGE, "gpio %d, bad range (%d)", gpio, range); oldWidth = gpioInfo[gpio].width; @@ -4247,7 +4308,10 @@ int gpioWaveTxStart(unsigned mode) cb = wave2Cbs(mode); if (gpioCfg.dbgLevel >= DBG_SLOW_TICK) + { + fprintf(stderr, "*** OUTPUT DMA CONTROL BLOCKS ***\n"); for (i=0; i */ /* -This version is for pigpio version 6 +This version is for pigpio version 7 */ #ifndef PIGPIO_H @@ -82,7 +82,7 @@ This version is for pigpio version 6 #include -#define PIGPIO_VERSION 6 +#define PIGPIO_VERSION 7 /*-------------------------------------------------------------------------*/ @@ -175,7 +175,9 @@ gpioHardwareRevision Get hardware version. gpioCfgBufferSize Configure the gpio sample buffer size. gpioCfgClock Configure the gpio sample rate. -gpioCfgDMAchannel Configure the DMA channel. +gpioCfgDMAchannel Configure the DMA channel (DEPRECATED). +gpioCfgDMAchannels Configure the DMA channels. +gpioCfgPermissions Configure the gpio access permissions. gpioCfgInterfaces Configure user interfaces. gpioCfgSocketPort Configure socket port. @@ -482,7 +484,7 @@ int gpioSetPWMrange(unsigned user_gpio, to gpioPWM will use a dutycycle between 0 (off) and range (fully on). Returns the real range for the given gpio's frequency if OK, - otherwise PI_BAD_USER_GPIO or PI_BAD_DUTY_RANGE. + otherwise PI_BAD_USER_GPIO or PI_BAD_DUTYRANGE. EXAMPLE: ... @@ -892,9 +894,12 @@ int gpioWaveAddSerial(unsigned user_gpio, the same waveform. */ +#define PI_WAVE_BLOCKS 3 +#define PI_WAVE_MAX_PULSES (PI_WAVE_BLOCKS * 3000) +#define PI_WAVE_MAX_CHARS (PI_WAVE_BLOCKS * 256) + #define PI_WAVE_MIN_BAUD 100 #define PI_WAVE_MAX_BAUD 250000 -#define PI_WAVE_MAX_CHARS 256 @@ -1601,7 +1606,7 @@ int gpioCfgClock(unsigned micros, /*-------------------------------------------------------------------------*/ -int gpioCfgDMAchannel(unsigned channel); +int gpioCfgDMAchannel(unsigned channel); /* DEPRECATED */ /*-------------------------------------------------------------------------*/ /* Configures pigpio to use the specified DMA channel. @@ -1622,7 +1627,7 @@ int gpioCfgDMAchannels(unsigned primaryChannel, /* Configures pigpio to use the specified DMA channels. The default setting is to use channel 14 for the primary channel and - channel 6 for the secondary channel. + channel 5 for the secondary channel. */ #define PI_MAX_PRIMARY_CHANNEL 14 @@ -1630,6 +1635,18 @@ int gpioCfgDMAchannels(unsigned primaryChannel, +/*-------------------------------------------------------------------------*/ +int gpioCfgPermissions(uint64_t updateMask); +/*-------------------------------------------------------------------------*/ +/* Configures pigpio to only allow updates (writes or mode changes) for the + gpios specified by the mask. + + The default setting is to allow updates to gpios 0-31, i.e. an update mask + of 0x00000000FFFFFFFF. +*/ + + + /*-------------------------------------------------------------------------*/ int gpioCfgSocketPort(unsigned port); /*-------------------------------------------------------------------------*/ @@ -1746,7 +1763,8 @@ after this command is issued. #define PI_BAD_CLK_SOURCE -18 /* clock source not 0-1 */ #define PI_BAD_CLK_MICROS -19 /* clock micros not 1, 2, 4, 5, 8, or 10 */ #define PI_BAD_BUF_MILLIS -20 /* buf millis not 100-10000 */ -#define PI_BAD_DUTY_RANGE -21 /* dutycycle range not 25-40000 */ +#define PI_BAD_DUTYRANGE -21 /* dutycycle range not 25-40000 */ +#define PI_BAD_DUTY_RANGE -21 /* DEPRECATED (use PI_BAD_DUTYRANGE) */ #define PI_BAD_SIGNUM -22 /* signum not 0-63 */ #define PI_BAD_PATHNAME -23 /* can't open pathname */ #define PI_NO_HANDLE -24 /* no handle available */ @@ -1767,6 +1785,8 @@ after this command is issued. #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_NOT_PERMITTED -41 /* gpio operation not permitted */ +#define PI_SOME_PERMITTED -42 /* one or more gpios not permitted */ /*-------------------------------------------------------------------------*/ @@ -1778,8 +1798,12 @@ after this command is issued. #define PI_DEFAULT_IF_FLAGS 0 #define PI_DEFAULT_DMA_CHANNEL 14 #define PI_DEFAULT_DMA_PRIMARY_CHANNEL 14 -#define PI_DEFAULT_DMA_SECONDARY_CHANNEL 6 +#define PI_DEFAULT_DMA_SECONDARY_CHANNEL 5 #define PI_DEFAULT_SOCKET_PORT 8888 +#define PI_DEFAULT_SOCKET_PORT_STR "8888" +#define PI_DEFAULT_SOCKET_ADDR_STR "127.0.0.1" +#define PI_DEFAULT_UPDATE_MASK_R0 0xFBE6CF9F +#define PI_DEFAULT_UPDATE_MASK_R1 0x03E6CF93 +#define PI_DEFAULT_UPDATE_MASK_R2 0xFBC6CF9C #endif - diff --git a/pigpio.py b/pigpio.py new file mode 100644 index 0000000..d4e7894 --- /dev/null +++ b/pigpio.py @@ -0,0 +1,1591 @@ +""" +pigpio is a Python module for the Raspberry Pi which allows control +of the general purpose input outputs (gpios). + +There are 54 gpios in total, arranged in two banks. Bank 1 contains +gpios 0-31. Bank 2 contains gpios 32-54. + +Most of the gpios are dedicated to system use. + +A user should only manipulate gpios in bank 1. + +For a Rev.1 board only use gpios 0, 1, 4, 7, 8, 9, 10, 11, 14, 15, 17, +18, 21, 22, 23, 24, 25. + +For a Rev.2 board only use gpios 2, 3, 4, 7, 8, 9, 10, 11, 14, 15, 17, +18, 22, 23, 24, 25, 27, 28, 29, 30, 31. + +It is safe to read all the gpios. If you try to write a system gpio or +change its mode you can crash the Pi or corrupt the data on the SD card. + +Features + +The pigpio module's main features are: + +- provision of PWM on any number of gpios 0-31 simultaneously. + +- provision of servo pulses on any number of gpios 0-31 simultaneously. + +- callbacks when any of gpios 0-31 change state. + +- reading/writing gpios and setting their modes (typically input + or output). + +- reading/writing all of the gpios in a bank (0-31, 32-53) as a single + operation. + +Notes + +ALL gpios are identified by their Broadcom number. + +This module uses the services of the C pigpio library. That library +must be running on the Pi whose gpios are to be manipulated. + +The normal way to start the library is as a daemon (during system +start). + +sudo pigpiod + +Your Python program should wrap the use of the module up in calls +to pigpio.start() and pigpio.stop(). + +Settings + +A number of settings are determined when the pigpiod daemon is started. + +- the sample rate (1, 2, 4, 5, 8, or 10us, default 5us). + +- the set of gpios which may be updated (generally written to). The + default set is those listed above for the Rev.1 or Rev.2 boards. + +- the available PWM frequencies (see set_PWM_frequency()). + +Exceptions + +By default a fatal exception is raised if you pass an invalid +argument to a pigpio function. + +If you wish to handle the returned status yourself you should set +pigpio.exceptions = False. + +""" +import socket +import struct +import time +import threading +import os +import atexit + +VERSION = "1.0" + +# gpio levels + +OFF = 0 +LOW = 0 +CLEAR = 0 + +ON = 1 +HIGH = 1 +SET = 1 + +TIMEOUT = 2 + +# gpio edges + +EITHER_EDGE = 0 +RISING_EDGE = 1 +FALLING_EDGE = 2 + +# gpio modes + +INPUT = 0 +OUTPUT = 1 +ALT0 = 4 +ALT1 = 5 +ALT2 = 6 +ALT3 = 7 +ALT4 = 3 +ALT5 = 2 + +# gpio Pull Up Down + +PUD_OFF = 0 +PUD_DOWN = 1 +PUD_UP = 2 + +# pigpio command numbers + +_PI_CMD_MODES= 0 +_PI_CMD_MODEG= 1 +_PI_CMD_PUD= 2 +_PI_CMD_READ= 3 +_PI_CMD_WRITE= 4 +_PI_CMD_PWM= 5 +_PI_CMD_PRS= 6 +_PI_CMD_PFS= 7 +_PI_CMD_SERVO= 8 +_PI_CMD_WDOG= 9 +_PI_CMD_BR1= 10 +_PI_CMD_BR2= 11 +_PI_CMD_BC1= 12 +_PI_CMD_BC2= 13 +_PI_CMD_BS1= 14 +_PI_CMD_BS2= 15 +_PI_CMD_TICK= 16 +_PI_CMD_HWVER=17 +_PI_CMD_NO= 18 +_PI_CMD_NB= 19 +_PI_CMD_NP= 20 +_PI_CMD_NC= 21 +_PI_CMD_PRG= 22 +_PI_CMD_PFG= 23 +_PI_CMD_PRRG= 24 +_PI_CMD_NOIB= 99 + +# pigpio error numbers + +_PI_INIT_FAILED =-1 +PI_BAD_USER_GPIO =-2 +PI_BAD_GPIO =-3 +PI_BAD_MODE =-4 +PI_BAD_LEVEL =-5 +PI_BAD_PUD =-6 +PI_BAD_PULSEWIDTH =-7 +PI_BAD_DUTYCYCLE =-8 +_PI_BAD_TIMER =-9 +_PI_BAD_MS =-10 +_PI_BAD_TIMETYPE =-11 +_PI_BAD_SECONDS =-12 +_PI_BAD_MICROS =-13 +_PI_TIMER_FAILED =-14 +PI_BAD_WDOG_TIMEOUT =-15 +_PI_NO_ALERT_FUNC =-16 +_PI_BAD_CLK_PERIPH =-17 +_PI_BAD_CLK_SOURCE =-18 +_PI_BAD_CLK_MICROS =-19 +_PI_BAD_BUF_MILLIS =-20 +PI_BAD_DUTYRANGE =-21 +_PI_BAD_SIGNUM =-22 +_PI_BAD_PATHNAME =-23 +PI_NO_HANDLE =-24 +PI_BAD_HANDLE =-25 +_PI_BAD_IF_FLAGS =-26 +_PI_BAD_CHANNEL =-27 +_PI_BAD_PRIM_CHANNEL=-27 +_PI_BAD_SOCKET_PORT =-28 +_PI_BAD_FIFO_COMMAND=-29 +_PI_BAD_SECO_CHANNEL=-30 +_PI_NOT_INITIALISED =-31 +_PI_INITIALISED =-32 +_PI_BAD_WAVE_MODE =-33 +_PI_BAD_CFG_INTERNAL=-34 +_PI_BAD_WAVE_BAUD =-35 +_PI_TOO_MANY_PULSES =-36 +_PI_TOO_MANY_CHARS =-37 +_PI_NOT_SERIAL_GPIO =-38 +_PI_BAD_SERIAL_STRUC=-39 +_PI_BAD_SERIAL_BUF =-40 +PI_NOT_PERMITTED =-41 +PI_SOME_PERMITTED =-42 + +# pigpio error text + +_errors=[ + [_PI_INIT_FAILED , "pigpio initialisation failed"], + [PI_BAD_USER_GPIO , "gpio not 0-31"], + [PI_BAD_GPIO , "gpio not 0-53"], + [PI_BAD_MODE , "mode not 0-7"], + [PI_BAD_LEVEL , "level not 0-1"], + [PI_BAD_PUD , "pud not 0-2"], + [PI_BAD_PULSEWIDTH , "pulsewidth not 0 or 500-2500"], + [PI_BAD_DUTYCYCLE , "dutycycle not 0-255"], + [_PI_BAD_TIMER , "timer not 0-9"], + [_PI_BAD_MS , "ms not 10-60000"], + [_PI_BAD_TIMETYPE , "timetype not 0-1"], + [_PI_BAD_SECONDS , "seconds < 0"], + [_PI_BAD_MICROS , "micros not 0-999999"], + [_PI_TIMER_FAILED , "gpioSetTimerFunc failed"], + [PI_BAD_WDOG_TIMEOUT , "timeout not 0-60000"], + [_PI_NO_ALERT_FUNC , "DEPRECATED"], + [_PI_BAD_CLK_PERIPH , "clock peripheral not 0-1"], + [_PI_BAD_CLK_SOURCE , "clock source not 0-1"], + [_PI_BAD_CLK_MICROS , "clock micros not 1, 2, 4, 5, 8, or 10"], + [_PI_BAD_BUF_MILLIS , "buf millis not 100-10000"], + [PI_BAD_DUTYRANGE , "dutycycle range not 25-40000"], + [_PI_BAD_SIGNUM , "signum not 0-63"], + [_PI_BAD_PATHNAME , "can't open pathname"], + [PI_NO_HANDLE , "no handle available"], + [PI_BAD_HANDLE , "unknown notify handle"], + [_PI_BAD_IF_FLAGS , "ifFlags > 3"], + [_PI_BAD_CHANNEL , "DMA channel not 0-14"], + [_PI_BAD_SOCKET_PORT , "socket port not 1024-30000"], + [_PI_BAD_FIFO_COMMAND , "unknown fifo command"], + [_PI_BAD_SECO_CHANNEL , "DMA secondary channel not 0-6"], + [_PI_NOT_INITIALISED , "function called before gpioInitialise"], + [_PI_INITIALISED , "function called after gpioInitialise"], + [_PI_BAD_WAVE_MODE , "waveform mode not 0-1"], + [_PI_BAD_CFG_INTERNAL , "bad parameter in gpioCfgInternals call"], + [_PI_BAD_WAVE_BAUD , "baud rate not 100-250000"], + [_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_NOT_PERMITTED , "no permission to update gpio"], + [PI_SOME_PERMITTED , "no permission to update one or more gpios"] +] + +_control = None +_notify = None + +_host = '' +_port = 8888 + +exceptions = True + +class _pigpioError(Exception): + """pigpio module exception""" + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) + +def error(pigpio_error): + """Converts a pigpio error number to a text description. + + pigpio_error: an error number (<0) returned by pigpio. + + Example + ... + print(pigpio.error(-5)) + level not 0-1 + ... + """ + for e in _errors: + if e[0] == pigpio_error: + return e[1] + return "unknown error" + +def tickDiff(tStart, tEnd): + """Calculate the time difference between two ticks. + + tStart: the earlier tick. + tEnd: the later tick. + + The function handles wrap around as the tick overflows 32 bits. + + The returned value is in microseconds. + + Example + ... + print(pigpio.tickDiff(4294967272, 12)) + 36 + ... + """ + tDiff = tEnd - tStart + if tDiff < 0: + tDiff += (1 << 32) + return tDiff + +def _u2i(number): + """Converts a number from unsigned to signed. + + number: a 32 bit unsigned number + """ + mask = (2 ** 32) - 1 + if number & (1 << 31): + v = number | ~mask + else: + v = number & mask + if v >= 0: + return v; + else: + if exceptions: + raise _pigpioError(error(v)) + else: + return v + +def _pigpio_command(sock, cmd, p1, p2): + """Executes a pigpio socket command. + + sock: command socket. + cmd: the command to be executed. + p1: command paramter 1 (if applicable). + p2: command paramter 2 (if applicable). + """ + if sock is not None: + sock.send(struct.pack('IIII', cmd, p1, p2, 0)) + x, y, z, res = struct.unpack('IIII', sock.recv(16)) + return res + else: + raise _pigpioError("*** Module not started, call pigpio.start() ***") + +class _callback: + """An ADT class to hold callback information.""" + + def __init__(self, gpio, edge, func): + """Initialises a callback ADT. + + gpio: Broadcom gpio number. + edge: EITHER_EDGE, RISING_EDGE, or FALLING_EDGE. + func: a user function taking three arguments (gpio, level, tick). + """ + self.gpio = gpio + self.edge = edge + self.func = func + self.bit = 1<= 0: + pigpio.notify_begin(h, 1234) + ... + """ + r=_u2i(_pigpio_command(_control, _PI_CMD_NO, 0, 0)) + return r + +def notify_begin(handle, 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. + + Example + ... + h = pigpio.notify_open() + if h >= 0: + pigpio.notify_begin(h, 1234) + ... + + This will start notifications for gpios 1, 4, 6, 7, 10 + (1234 = 0x04D2 = 0b0000010011010010). + + 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 + + """ + r=_u2i(_pigpio_command(_control, _PI_CMD_NB, handle, bits)) + return r + +def notify_pause(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. + + Example + ... + h = pigpio.notify_open() + if h >= 0: + pigpio.notify_begin(h, 1234) + ... + pigpio.notify_pause(h) + ... + pigpio.notify_begin(h, 1234) + ... + ... + """ + r=_u2i(_pigpio_command(_control, _PI_CMD_NB, handle, 0)) + return r + +def notify_close(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()) + + Example + ... + h = pigpio.notify_open() + if h >= 0: + pigpio.notify_begin(h, 1234) + ... + pigpio.notify_close(h) + ... + ... + """ + r=_u2i(_pigpio_command(_control, _PI_CMD_NC, handle, 0)) + return r + +def set_watchdog(user_gpio, 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() class interprets the flags and will + call registered callbacks for the gpio with level TIMEOUT. + + Example + + #!/usr/bin/python + + import pigpio + import time + + def cbf(g, L, t): + message = "gpio=" + str(g) + " level=" + str(L) + " at " + str(t) + print(message) + + pigpio.start() + + cb = pigpio.callback(22, pigpio.EITHER_EDGE, cbf) + + print("callback started, 5 second delay") + + time.sleep(5) + + pigpio.set_watchdog(22, 1000) # 1000ms watchdog + + print("watchdog started, 5 second delay") + + time.sleep(5) + + pigpio.set_watchdog(22, 0) # cancel watchdog + + print("watchdog cancelled, 5 second delay") + + time.sleep(5) + + cb.cancel() + + pigpio.stop() + + will print lines such as + + callback started, 5 second delay + watchdog started, 5 second delay + gpio=22 level=2 at 3547411617 + gpio=22 level=2 at 3548411254 + gpio=22 level=2 at 3549411927 + gpio=22 level=2 at 3550412060 + gpio=22 level=2 at 3551411622 + watchdog cancelled, 5 second delay + """ + r=_u2i(_pigpio_command(_control, _PI_CMD_WDOG, user_gpio, timeout)) + return r + +def read_bank_1(): + """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< */ /* -This version is for pigpio version 4+ +This version is for pigpio version 7+ */ #include @@ -54,8 +54,10 @@ static unsigned clockMicros = PI_DEFAULT_CLK_MICROS; static unsigned clockPeripheral = PI_DEFAULT_CLK_PERIPHERAL; static unsigned clockSource = PI_DEFAULT_CLK_SOURCE; static unsigned ifFlags = PI_DEFAULT_IF_FLAGS; -static unsigned DMAchannelChannel = PI_DEFAULT_DMA_CHANNEL; +static unsigned DMAprimaryChannel = PI_DEFAULT_DMA_PRIMARY_CHANNEL; +static unsigned DMAsecondaryChannel = PI_DEFAULT_DMA_SECONDARY_CHANNEL; static unsigned socketPort = PI_DEFAULT_SOCKET_PORT; +static uint64_t updateMask = -1; static FILE * errFifo; @@ -64,13 +66,15 @@ void usage() fprintf(stderr, "\n" \ "Usage: sudo pigpiod [OPTION] ...\n" \ " -b value, gpio sample buffer in milliseconds, default 120\n" \ - " -d value, DMA channel, 0-14, default 14\n" \ + " -d value, primary DMA channel, 0-14, default 14\n" \ + " -e value, secondary DMA channel, 0-6, default 5\n" \ " -f, disable fifo interface, default enabled\n" \ " -k, disable socket interface, default enabled\n" \ " -p value, socket port, 1024-32000, default 8888\n" \ " -s value, sample rate, 1, 2, 4, 5, 8, or 10, default 5\n" \ " -t value, clock peripheral, 0=PWM 1=PCM, default PCM\n" \ " -u value, clock source, 0=OSC 1=PLLD, default PLLD\n" \ + " -x mask, gpios which may be updated, default 0xFFFFFFFF\n" \ "EXAMPLE\n" \ "sudo pigpiod -s 2 -b 200 -f\n" \ " Set a sample rate of 2 microseconds with a 200 millisecond\n" \ @@ -81,8 +85,10 @@ void usage() static void initOpts(int argc, char *argv[]) { int i, opt; + uint64_t mask; + char * endptr; - while ((opt = getopt(argc, argv, "b:d:fkp:s:t:u:")) != -1) + while ((opt = getopt(argc, argv, "b:d:e:fkp:s:t:u:x:")) != -1) { i = -1; @@ -97,11 +103,18 @@ static void initOpts(int argc, char *argv[]) case 'd': i = atoi(optarg); - if ((i >= PI_MIN_DMA_CHANNEL) && (i <= PI_MAX_DMA_CHANNEL)) - DMAchannelChannel = i; + if ((i >= PI_MIN_DMA_CHANNEL) && (i <= PI_MAX_PRIMARY_CHANNEL)) + DMAprimaryChannel = i; else cmdFatal("invalid -d option (%d)", i); break; + case 'e': + i = atoi(optarg); + if ((i >= PI_MIN_DMA_CHANNEL) && (i <= PI_MAX_SECONDARY_CHANNEL)) + DMAsecondaryChannel = i; + else cmdFatal("invalid -e option (%d)", i); + break; + case 'f': ifFlags |= PI_DISABLE_FIFO_IF; break; @@ -151,6 +164,13 @@ static void initOpts(int argc, char *argv[]) else cmdFatal("invalid -u option (%d)", i); break; + case 'x': + mask = strtoll(optarg, &endptr, 0); + printf("mask=%llx\n", mask); + if (!*endptr) updateMask = mask; + else cmdFatal("invalid -x option (%s)", optarg); + break; + default: /* '?' */ usage(); exit(-1); @@ -224,10 +244,12 @@ int main(int argc, char **argv) gpioCfgInterfaces(ifFlags); - gpioCfgDMAchannel(DMAchannelChannel); + gpioCfgDMAchannels(DMAprimaryChannel, DMAsecondaryChannel); gpioCfgSocketPort(socketPort); + if (updateMask != -1) gpioCfgPermissions(updateMask); + /* start library */ if (gpioInitialise()< 0) cmdFatal("Can't initialise pigpio library"); @@ -275,4 +297,3 @@ int main(int argc, char **argv) return 0; } - diff --git a/pigs.c b/pigs.c index e794632..7115304 100644 --- a/pigs.c +++ b/pigs.c @@ -26,7 +26,7 @@ For more information, please refer to */ /* -This version is for pigpio version 3+ +This version is for pigpio version 7+ */ #include @@ -34,6 +34,8 @@ This version is for pigpio version 3+ #include #include #include +#include +#include #include #include "pigpio.h" @@ -44,100 +46,123 @@ This program provides a socket interface to the commands available from pigpio. */ +static int openSocket(void) +{ + int sock, err; + struct addrinfo hints, *res, *rp; + const char *addrStr, *portStr; + + portStr = getenv(PI_ENVPORT); + + if (!portStr) portStr = PI_DEFAULT_SOCKET_PORT_STR; + + addrStr = getenv(PI_ENVADDR); + + if (!addrStr) addrStr = PI_DEFAULT_SOCKET_ADDR_STR; + + 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 -1; + + 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 -1; + + return sock; +} + int main(int argc , char *argv[]) { - int sock, r, idx, port; - struct sockaddr_in server; + int sock, r, idx; cmdCmd_t cmd; - char * portStr, * addrStr; char buf[128]; - - sock = socket(AF_INET, SOCK_STREAM, 0); - + + sock = openSocket(); + if (sock != -1) { - portStr = getenv(PI_ENVPORT); - - if (portStr) port = atoi(portStr); - else port = PI_DEFAULT_SOCKET_PORT; - - addrStr = getenv(PI_ENVADDR); - - if (!addrStr) addrStr="127.0.0.1"; - - server.sin_addr.s_addr = inet_addr(addrStr); - server.sin_family = AF_INET; - server.sin_port = htons(port); - - if (connect(sock, (struct sockaddr *)&server, sizeof(server)) == 0) + switch(argc) { - switch(argc) - { - case 1: - exit(0); + case 1: + exit(0); - case 2: - sprintf(buf, "%10s", argv[1]); - break; + case 2: + sprintf(buf, "%10s", argv[1]); + break; - case 3: - sprintf(buf, "%10s %10s", argv[1], argv[2]); - break; + case 3: + sprintf(buf, "%10s %10s", argv[1], argv[2]); + break; - case 4: - sprintf(buf, "%10s %10s %10s", argv[1], argv[2], argv[3]); - break; + case 4: + sprintf(buf, "%10s %10s %10s", argv[1], argv[2], argv[3]); + break; - default: - cmdFatal("what?"); - } - - if ((idx=cmdParse(buf, &cmd)) >= 0) - { - if (send(sock, &cmd, sizeof(cmdCmd_t), 0) == sizeof(cmdCmd_t)) - { - if (recv(sock, &cmd, sizeof(cmdCmd_t), 0) == sizeof(cmdCmd_t)) - { - switch (cmdInfo[idx].rv) - { - case 0: - r = cmd.res; - if (r < 0) cmdFatal("ERROR: %s", cmdErrStr(r)); - break; - - case 1: - break; - - case 2: - r = cmd.res; - if (r < 0) cmdFatal("ERROR: %s", cmdErrStr(r)); - else printf("%d\n", r); - break; - - case 3: - printf("%08X\n", cmd.res); - break; - - case 4: - printf("%u\n", cmd.res); - break; - - case 5: - printf(cmdUsage); - break; - } - } - else cmdFatal("recv failed, %m"); - } - else cmdFatal("send failed, %m"); - } - else cmdFatal("what?"); + default: + cmdFatal("what?"); } - else cmdFatal("connect failed, %m"); - close(sock); + if ((idx=cmdParse(buf, &cmd)) >= 0) + { + if (send(sock, &cmd, sizeof(cmdCmd_t), 0) == sizeof(cmdCmd_t)) + { + if (recv(sock, &cmd, sizeof(cmdCmd_t), 0) == sizeof(cmdCmd_t)) + { + switch (cmdInfo[idx].rv) + { + case 0: + r = cmd.res; + if (r < 0) cmdFatal("ERROR: %s", cmdErrStr(r)); + break; + + case 1: + r = cmd.res; + if (r < 0) cmdFatal("ERROR: %s", cmdErrStr(r)); + break; + + case 2: + r = cmd.res; + if (r < 0) cmdFatal("ERROR: %s", cmdErrStr(r)); + else printf("%d\n", r); + break; + + case 3: + printf("%08X\n", cmd.res); + break; + + case 4: + printf("%u\n", cmd.res); + break; + + case 5: + printf(cmdUsage); + break; + } + } + else cmdFatal("recv failed, %m"); + } + else cmdFatal("send failed, %m"); + } + else cmdFatal("what?"); } - else cmdFatal("socket failed, %m"); + else cmdFatal("connect failed, %m"); + + close(sock); return 0; } diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..ceff7d9 --- /dev/null +++ b/setup.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +from distutils.core import setup + +setup(name='pigpio', + version='1.0', + description='Raspberry Pi gpio utility', + author='joan', + author_email='joan@abyz.me.uk', + url='http://abyz.co.uk/rpi/pigpio/python.html/', + py_modules=['pigpio'] + )