V13
authorjoan <joan@abyz.me.uk>
Thu, 13 Mar 2014 15:50:23 +0000 (15:50 +0000)
committerjoan <joan@abyz.me.uk>
Thu, 13 Mar 2014 15:50:23 +0000 (15:50 +0000)
16 files changed:
Makefile
README
command.c
command.h
pigpio.c
pigpio.h
pigpio.py
pigpiod_if.c
pigpiod_if.h
pigs.c
setup.py
x_pigpio.c [new file with mode: 0644]
x_pigpio.py [new file with mode: 0755]
x_pigpiod_if.c [new file with mode: 0644]
x_pigs [new file with mode: 0755]
x_pipe [new file with mode: 0755]

index 9b1e561e3236a852baa4cb13dac37688e5ca4a42..bfb0beb35510d6b2014bf8aaaf3bc4b86cb28dad 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -13,17 +13,22 @@ OBJ2     = pigpiod_if.o command.o
 
 LIB      = $(LIB1) $(LIB2)
 
-ALL     = $(LIB) checklib pig2vcd pigpiod pigs
+ALL     = $(LIB) x_pigpio x_pigpiod_if pig2vcd pigpiod pigs
 
-LL      = -L. -lpigpio -lpthread -lrt
+LL1      = -L. -lpigpio -lpthread -lrt
+
+LL2      = -L. -lpigpiod_if -lpthread -lrt
 
 all:   $(ALL)
 
-checklib:      checklib.o $(LIB1)
-       $(CC) -o checklib checklib.c $(LL)
+x_pigpio:      x_pigpio.o $(LIB1)
+       $(CC) -o x_pigpio x_pigpio.c $(LL1)
+
+x_pigpiod_if:  x_pigpiod_if.o $(LIB2)
+       $(CC) -o x_pigpiod_if x_pigpiod_if.c $(LL2)
 
 pigpiod:       pigpiod.o $(LIB1)
-       $(CC) -o pigpiod pigpiod.c $(LL)
+       $(CC) -o pigpiod pigpiod.c $(LL1)
 
 pigs:          pigs.o command.o
        $(CC) -o pigs pigs.c command.c
@@ -35,6 +40,7 @@ clean:
        rm -f *.o *.i *.s *~ $(ALL)
 
 install:       $(LIB) 
+       sudo install -m 0755 -d              /opt/pigpio/cgi
        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
@@ -68,7 +74,8 @@ $(LIB2):      $(OBJ2)
 
 # generated using gcc -MM *.c
 
-checklib.o: checklib.c pigpio.h
+x_pigpio.o: x_pigpio.c pigpio.h
+x_pigpiod_if.o: x_pigpiod_if.c
 command.o: command.c pigpio.h command.h
 pig2vcd.o: pig2vcd.c pigpio.h
 pigpio.o: pigpio.c pigpio.h command.h
diff --git a/README b/README
index 8737d361931f4230464509753aaea91b5a9fce1d..d04da3cc91c8170865c65691434ade0c5d6f915e 100644 (file)
--- a/README
+++ b/README
@@ -20,18 +20,28 @@ o the socket interface (pigs) in /usr/local/bin
 o the utility pig2vcd in /usr/local/bin
 o the Python module pigpio.py
 
-TEST
+TEST (optional)
 
 To test the library do
 
-sudo ./checklib
+sudo ./x_pigpio
+
+To text the pigpio daemon do
+
+sudo pigpiod
+
+./x_pigpiod_if # test the C I/F to the pigpio daemon
+./x_pigpio.py  # test the Python I/F to the pigpio daemon
+./x_pigs       # test the socket I/F to the pigpio daemon
+./x_pipe       # test the pipe I/F to the pigpio daemon
 
 EXAMPLE CODE
 
-checklib.c, pig2vcd.c, and pigpiod.c
-show examples of interfacing with the pigpio library.
+x_pigpio.c, pig2vcd.c, and pigpiod.c show examples of interfacing
+with the pigpio library.
 
-pigs.c and pigpio.py show examples of interfacing with the pigpiod daemon.
+pigs.c, pigpio.py, x_pigpiod_if.c, x_pigpio.py, x_pigs, and x_pipe
+show examples of interfacing with the pigpiod daemon.
 
 DAEMON
 
@@ -77,25 +87,6 @@ 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.
-
-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 pigpiod daemon
@@ -105,7 +96,7 @@ 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.
+is connected to it over the network.  This access is via the socket interface.
 
 In particular this allows you to use the following on non-Pi's.
 
index 757b0824127509b938a49795c23b2e1a63208ae1..9bf8f9bdc21d8543cb7693472dd120882c0de43e 100644 (file)
--- a/command.c
+++ b/command.c
@@ -26,7 +26,7 @@ For more information, please refer to <http://unlicense.org/>
 */
 
 /*
-This version is for pigpio version 12+
+This version is for pigpio version 13+
 */
 
 #include <stdio.h>
@@ -58,135 +58,204 @@ This version is for pigpio version 12+
  7 cmd   %x
  8 MODES %d %c
  9 PUD   %d %c
-10 PROG  %s
+10 PROC  %s
 11 WVAS  %d %d %d %s
+12 PROCR %d [%d]*10
+13 WVAG  %d %d %d [%d %d %d]*160
+
+
+20 cmd m(0-150)
+21 cmd v
+22 cmd L
+23 cmd
+24 cmd m1(0-150) m2(0-150)
+25 cmd p(0-9)
 */
 
 cmdInfo_t cmdInfo[]=
 {
-   /* num         str   vfyt retv ext */
-
-   {PI_CMD_BC1,   "BC1",   7, 1,  0},
-   {PI_CMD_BC2,   "BC2",   7, 1,  0},
-   {PI_CMD_BR1,   "BR1",   1, 3,  0},
-   {PI_CMD_BR2,   "BR2",   1, 3,  0},
-   {PI_CMD_BS1,   "BS1",   7, 1,  0},
-   {PI_CMD_BS2,   "BS2",   7, 1,  0},
-   {PI_CMD_HELP,  "H",     6, 5,  0},
-   {PI_CMD_HELP,  "HELP",  6, 5,  0},
-   {PI_CMD_HWVER, "HWVER", 1, 4,  0},
-   {PI_CMD_MODEG, "MG"   , 2, 2,  0},
-   {PI_CMD_MODEG, "MODEG", 2, 2,  0},
-   {PI_CMD_MODES, "M",     8, 0,  0},
-   {PI_CMD_MODES, "MODES", 8, 0,  0},
-   {PI_CMD_NB,    "NB",    4, 0,  0},
-   {PI_CMD_NC,    "NC",    2, 0,  0},
-   {PI_CMD_NO,    "NO",    1, 2,  0},
-   {PI_CMD_NP,    "NP",    2, 0,  0},
-   {PI_CMD_PFG,   "PFG",   2, 2,  0},
-   {PI_CMD_PFS,   "PFS",   3, 2,  0},
-   {PI_CMD_PIGPV, "PIGPV", 1, 4,  0},
-   {PI_CMD_PRG,   "PRG",   2, 2,  0},
-   {PI_CMD_PROC,  "PROC", 10, 2,  1},
-   {PI_CMD_PROCD, "PROCD", 2, 2,  0},
-   {PI_CMD_PROCR, "PROCR", 2, 2,  0},
-   {PI_CMD_PROCS, "PROCS", 2, 2,  0},
-   {PI_CMD_PRRG,  "PRRG",  2, 2,  0},
-   {PI_CMD_PRS,   "PRS",   3, 2,  0},
-   {PI_CMD_PUD,   "PUD",   9, 0,  0},
-   {PI_CMD_PWM,   "P",     3, 0,  0},
-   {PI_CMD_PWM,   "PWM",   3, 0,  0},
-   {PI_CMD_READ,  "R",     2, 2,  0},
-   {PI_CMD_READ,  "READ",  2, 2,  0},
-   {PI_CMD_SERVO, "S",     3, 0,  0},
-   {PI_CMD_SERVO, "SERVO", 3, 0,  0},
-   {PI_CMD_SLR,   "SLR",   3, 6,  0},
-   {PI_CMD_SLRC,  "SLRC",  2, 2,  0},
-   {PI_CMD_SLRO,  "SLRO",  3, 2,  0},
-   {PI_CMD_WDOG,  "WDOG",  3, 0,  0},
-   {PI_CMD_WRITE, "W",     3, 0,  0},
-   {PI_CMD_WRITE, "WRITE", 3, 0,  0},
-   {PI_CMD_TICK,  "T",     1, 4,  0},
-   {PI_CMD_TICK,  "TICK",  1, 4,  0},
-   {PI_CMD_TRIG,  "TRIG",  5, 0,  1},
-   {PI_CMD_WVAS,  "WVAS", 11, 2,  3},
-   {PI_CMD_WVBSY, "WVBSY", 1, 2,  0},
-   {PI_CMD_WVCLR, "WVCLR", 1, 2,  0},
-   {PI_CMD_WVGO,  "WVGO" , 1, 2,  0},
-   {PI_CMD_WVGOR, "WVGOR", 1, 2,  0},
-   {PI_CMD_WVHLT, "WVHLT", 1, 2,  0},
-   {PI_CMD_WVSC,  "WVSC",  2, 2,  0},
-   {PI_CMD_WVSM,  "WVSM",  2, 2,  0},
-   {PI_CMD_WVSP,  "WVSP",  2, 2,  0},
+   /* num         str   vfyt retv */
+
+   {PI_CMD_BC1,   "BC1",   7, 1},
+   {PI_CMD_BC2,   "BC2",   7, 1},
+   {PI_CMD_BR1,   "BR1",   1, 3},
+   {PI_CMD_BR2,   "BR2",   1, 3},
+   {PI_CMD_BS1,   "BS1",   7, 1},
+   {PI_CMD_BS2,   "BS2",   7, 1},
+   {PI_CMD_HELP,  "H",     6, 5},
+   {PI_CMD_HELP,  "HELP",  6, 5},
+   {PI_CMD_HWVER, "HWVER", 1, 4},
+   {PI_CMD_MICRO, "MICRO", 21, 0},
+   {PI_CMD_MILLI, "MILLI", 21, 0},
+   {PI_CMD_MODEG, "MG"   , 2, 2},
+   {PI_CMD_MODEG, "MODEG", 2, 2},
+   {PI_CMD_MODES, "M",     8, 0},
+   {PI_CMD_MODES, "MODES", 8, 0},
+   {PI_CMD_NB,    "NB",    4, 0},
+   {PI_CMD_NC,    "NC",    2, 0},
+   {PI_CMD_NO,    "NO",    1, 2},
+   {PI_CMD_NP,    "NP",    2, 0},
+   {PI_CMD_PFG,   "PFG",   2, 2},
+   {PI_CMD_PFS,   "PFS",   3, 2},
+   {PI_CMD_PIGPV, "PIGPV", 1, 4},
+   {PI_CMD_PRG,   "PRG",   2, 2},
+   {PI_CMD_PROC,  "PROC", 10, 2},
+   {PI_CMD_PROCD, "PROCD", 2, 2},
+   {PI_CMD_PROCP, "PROCP", 2, 7},
+   {PI_CMD_PROCR, "PROCR",12, 2},
+   {PI_CMD_PROCS, "PROCS", 2, 2},
+   {PI_CMD_PRRG,  "PRRG",  2, 2},
+   {PI_CMD_PRS,   "PRS",   3, 2},
+   {PI_CMD_PUD,   "PUD",   9, 0},
+   {PI_CMD_PWM,   "P",     3, 0},
+   {PI_CMD_PWM,   "PWM",   3, 0},
+   {PI_CMD_READ,  "R",     2, 2},
+   {PI_CMD_READ,  "READ",  2, 2},
+   {PI_CMD_SERVO, "S",     3, 0},
+   {PI_CMD_SERVO, "SERVO", 3, 0},
+   {PI_CMD_SLR,   "SLR",   3, 6},
+   {PI_CMD_SLRC,  "SLRC",  2, 2},
+   {PI_CMD_SLRO,  "SLRO",  3, 2},
+   {PI_CMD_TICK,  "T",     1, 4},
+   {PI_CMD_TICK,  "TICK",  1, 4},
+   {PI_CMD_TRIG,  "TRIG",  5, 0},
+   {PI_CMD_WDOG,  "WDOG",  3, 0},
+   {PI_CMD_WRITE, "W",     3, 0},
+   {PI_CMD_WRITE, "WRITE", 3, 0},
+   {PI_CMD_WVAG,  "WVAG", 13, 2},
+   {PI_CMD_WVAS,  "WVAS", 11, 2},
+   {PI_CMD_WVBSY, "WVBSY", 1, 2},
+   {PI_CMD_WVCLR, "WVCLR", 1, 2},
+   {PI_CMD_WVGO,  "WVGO" , 1, 2},
+   {PI_CMD_WVGOR, "WVGOR", 1, 2},
+   {PI_CMD_WVHLT, "WVHLT", 1, 2},
+   {PI_CMD_WVSC,  "WVSC",  2, 2},
+   {PI_CMD_WVSM,  "WVSM",  2, 2},
+   {PI_CMD_WVSP,  "WVSP",  2, 2},
+
+   {PI_CMD_ADDI , "ADDI" , 21, 0},
+   {PI_CMD_ADDV , "ADDV" , 20, 0},
+   {PI_CMD_ANDI , "ANDI" , 21, 0},
+   {PI_CMD_ANDV , "ANDV" , 20, 0},
+   {PI_CMD_CALL , "CALL" , 22, 0},
+   {PI_CMD_CMPI , "CMPI" , 21, 0},
+   {PI_CMD_CMPV , "CMPV" , 20, 0},
+   {PI_CMD_DCRA , "DCRA" , 23, 0},
+   {PI_CMD_DCRV , "DCRV" , 20, 0},
+   {PI_CMD_HALT , "HALT" , 23, 0},
+   {PI_CMD_INRA , "INRA" , 23, 0},
+   {PI_CMD_INRV , "INRV" , 20, 0},
+   {PI_CMD_JM   , "JM"   , 22, 0},
+   {PI_CMD_JMP  , "JMP"  , 22, 0},
+   {PI_CMD_JNZ  , "JNZ"  , 22, 0},
+   {PI_CMD_JP   , "JP"   , 22, 0},
+   {PI_CMD_JZ   , "JZ"   , 22, 0},
+   {PI_CMD_LABEL, "LABEL", 22, 0},
+   {PI_CMD_LDAI , "LDAI" , 21, 0},
+   {PI_CMD_LDAP , "LDAP" , 25, 0},
+   {PI_CMD_LDAV , "LDAV" , 20, 0},
+   {PI_CMD_LDPA , "LDPA" , 25, 0},
+   {PI_CMD_LDVA , "LDVA" , 20, 0},
+   {PI_CMD_LDVI , "LDVI" , 28, 0},
+   {PI_CMD_LDVV , "LDVV" , 24, 0},
+   {PI_CMD_ORI  , "ORI"  , 21, 0},
+   {PI_CMD_ORV  , "ORV"  , 20, 0},
+   {PI_CMD_POPA , "POPA" , 23, 0},
+   {PI_CMD_POPV , "POPV" , 20, 0},
+   {PI_CMD_PUSHA, "PUSHA", 23, 0},
+   {PI_CMD_PUSHV, "PUSHV", 20, 0},
+   {PI_CMD_RET  , "RET"  , 23, 0},
+   {PI_CMD_RAL  , "RAL"  , 21, 0},
+   {PI_CMD_RAR  , "RAR"  , 21, 0},
+   {PI_CMD_SUBI , "SUBI" , 21, 0},
+   {PI_CMD_SUBV , "SUBV" , 20, 0},
+   {PI_CMD_SWAPA, "SWAPA", 20, 0},
+   {PI_CMD_SWAPV, "SWAPV", 24, 0},
+   {PI_CMD_SYS  , "SYS"  , 27, 0},
+   {PI_CMD_WAITI, "WAITI", 21, 0},
+   {PI_CMD_WAITV, "WAITV", 20, 0},
+   {PI_CMD_XORI , "XORI" , 21, 0},
+   {PI_CMD_XORV , "XORV" , 20, 0},
+
 };
 
 char * cmdUsage = "\
-BC1 x        Clear gpios x in bank 1.\n\
-BC2 x        Clear gpios x in bank 2.\n\
-BR1          Read gpios bank 1.\n\
-BR2          Read gpios bank 2.\n\
-BS1 x        Set gpios x in bank 1.\n\
-BS2 x        Set gpios x in bank 2.\n\
-H            Displays command help.\n\
-HELP         Displays command help.\n\
-HWVER        Return hardware version.\n\
-M g m        Set gpio g to mode m.\n\
-MG g         Get gpio g mode.\n\
-MODEG g      Get gpio g mode.\n\
-MODES g m    Set gpio g to mode m.\n\
-NB h x       Start notifications on handle h with x.\n\
-NC h         Close notification handle h.\n\
-NO           Request notification handle.\n\
-NP h         Pause notifications on handle h.\n\
-P u d        Set PWM value for user gpio u to d.\n\
-PFG u        Get PWM frequency for user gpio u.\n\
-PFS u d      Set PWM frequency for user gpio u to d.\n\
-PIGPV        Return pigpio version.\n\
-PRG u        Get PWM range for user gpio u.\n\
-PROC t       Store text t of script.\n\
-PROCD s      Delete script s.\n\
-PROCR s      Run script s.\n\
-PROCS s      Stop script s.\n\
-PRRG u       Get PWM real range for user gpio u.\n\
-PRS u d      Set PWM range for user gpio u to d.\n\
-PUD g p      Set gpio pull up/down for gpio g to p.\n\
-PWM u d      Set PWM value for user gpio u to d.\n\
-R g          Read gpio g.\n\
-READ g       Read gpio g.\n\
-S u d        Set servo value for user gpio u to d microseconds.\n\
-SERVO u d    Set servo value for user gpio u to d microseconds.\n\
-SLR u d      Read up to d bytes of serial data from user gpio u.\n\
-SLRC u       Close user gpio u for serial data.\n\
-SLRO u b     Open user gpio u for serial data at b baud.\n\
-T            Return current tick.\n\
-TICK         Return current tick.\n\
-TRIG u pl L  Trigger level L for pl micros on user gpio u.\n\
-W g L        Write level L to gpio g.\n\
-WDOG u d     Set watchdog of d milliseconds on user gpio u.\n\
-WRITE g L    Write level L to gpio g.\n\
-WVAS u b o t Wave add serial data t to user gpio u at b baud.\n\
-WVBSY        Check if wave busy.\n\
-WVCLR        Wave clear.\n\
-WVGO         Wave transmit.\n\
-WVGOR        Wave transmit repeatedly.\n\
-WVHLT        Wave stop.\n\
-WVSC ws      Wave get DMA control block stats.\n\
-WVSM ws      Wave get micros stats.\n\
-WVSP ws      Wave get pulses stats.\n\
-.\n\
+BC1 v         Clear gpios defined by mask v in bank 1.\n\
+BC2 v         Clear gpios defined by mask v in bank 2.\n\
+BR1           Read gpios bank 1.\n\
+BR2           Read gpios bank 2.\n\
+BS1 v         Set gpios defined by mask v in bank 1.\n\
+BS2 v         Set gpios defined by mask v in bank 2.\n\
+H             Displays command help.\n\
+HELP          Displays command help.\n\
+HWVER         Return hardware version.\n\
+M g m         Set gpio g to mode m.\n\
+MG g          Get gpio g mode.\n\
+MICRO v       Delay for v microseconds.\n\
+MILLI v       Delay for v milliseconds.\n\
+MODEG g       Get gpio g mode.\n\
+MODES g m     Set gpio g to mode m.\n\
+NB h v        Start notifications on handle h for gpios defined by mask v.\n\
+NC h          Close notification handle h.\n\
+NO            Request notification handle.\n\
+NP h          Pause notifications on handle h.\n\
+P u v         Set PWM value for user gpio u to d.\n\
+PFG u         Get PWM frequency for user gpio u.\n\
+PFS u v       Set PWM frequency for user gpio u to d.\n\
+PIGPV         Return pigpio version.\n\
+PRG u         Get PWM range for user gpio u.\n\
+PROC t        Store text t of script.\n\
+PROCD s       Delete script s.\n\
+PROCP s       Get current status and parameter values for script s.\n\
+PROCR s pars  Run script s with up to 10 optional parameters.\n\
+PROCS s       Stop script s.\n\
+PRRG u        Get PWM real range for user gpio u.\n\
+PRS u v       Set PWM range for user gpio u to d.\n\
+PUD g p       Set gpio pull up/down for gpio g to p.\n\
+PWM u v       Set PWM value for user gpio u to d.\n\
+R g           Read gpio g.\n\
+READ g        Read gpio g.\n\
+S u v         Set servo value for user gpio u to d microseconds.\n\
+SERVO u v     Set servo value for user gpio u to d microseconds.\n\
+SLR u v       Read up to d bytes of serial data from user gpio u.\n\
+SLRC u        Close user gpio u for serial data.\n\
+SLRO u b      Open user gpio u for serial data at b baud.\n\
+T             Return current tick.\n\
+TICK          Return current tick.\n\
+TRIG u pl L   Trigger level L for pl micros on user gpio u.\n\
+W g L         Write level L to gpio g.\n\
+WDOG u v      Set watchdog of d milliseconds on user gpio u.\n\
+WRITE g L     Write level L to gpio g.\n\
+WVAG pulses   Wave add generic pulses.\n\
+WVAS u b o t  Wave add serial data t to user gpio u at b baud.\n\
+WVBSY         Check if wave busy.\n\
+WVCLR         Wave clear.\n\
+WVGO          Wave transmit.\n\
+WVGOR         Wave transmit repeatedly.\n\
+WVHLT         Wave stop.\n\
+WVSC ws       Wave get DMA control block stats.\n\
+WVSM ws       Wave get micros stats.\n\
+WVSP ws       Wave get pulses stats.\n\
+\n\
 b = baud rate.\n\
-d = decimal value.\n\
-g = gpio (0-53).\n\
+g = any gpio (0-53).\n\
 h = handle (0-31).\n\
 L = level (0-1).\n\
 m = mode (RW540123).\n\
+mask = a bit mask where (1<<g) is set for each gpio g of interest.\n\
 o = offset (0-).\n\
 p = pud (ODU).\n\
+pars = 0 to 10 parameters for script.\n\
 pl = pulse length (0-100).\n\
-s = script id.\n\
+pulses = 1 or more triplets of gpios on, gpios off, delay.\n\
+s = script id (0-31).\n\
 t = text.\n\
 u = user gpio (0-31).\n\
-x = hex value.\n\
+v = value.\n\
+ws = 0=now, 1=high, 2=max.\n\
+\n\
+Numbers may be entered as hex (prefix 0x), octal (prefix 0),\n\
+otherwise they are assumed to be decimal.\n\
 ";
 
 typedef struct
@@ -248,12 +317,24 @@ static errInfo_t errInfo[]=
    {PI_BAD_SER_OFFSET   , "add serial data offset > 30 minute"},
    {PI_GPIO_IN_USE      , "gpio already in use"},
    {PI_BAD_SERIAL_COUNT , "must read at least a byte at a time"},
+   {PI_BAD_PARAM_NUM    , "script parameter must be 0-9"},
+   {PI_DUP_LABEL        , "script has duplicate label"},
+   {PI_TOO_MANY_LABELS  , "script has too many labels"},
+   {PI_BAD_SCRIPT_CMD   , "illegal script command"},
+   {PI_BAD_VAR_NUM      , "script variable must be 0-149"},
+   {PI_NO_SCRIPT_ROOM   , "no more room for scripts"},
+   {PI_NO_MEMORY        , "can't allocate temporary memory"},
+   {PI_SOCK_READ_FAILED , "socket read failed"},
+   {PI_SOCK_WRIT_FAILED , "socket write failed"},
+   {PI_TOO_MANY_PARAM   , "too many script parameters > 10"},
+   {PI_NOT_HALTED       , "script already running or failed"},
+
 };
 
 static char * fmtMdeStr="RW540123";
 static char * fmtPudStr="ODU";
 
-static int cmdMatch(char * str)
+static int cmdMatch(char *str)
 {
    int i;
 
@@ -264,65 +345,117 @@ static int cmdMatch(char * str)
    return -1;
 }
 
-int cmdParse(char *buf, cmdCmd_t *cmd, int argc, char *argv[], gpioExtent_t *ext)
+static int getNum(char *str, unsigned *val, uint8_t *opt, int flags)
 {
-   char str[8];
-   int f, valid, idx, val, p;
+   int f, n, v;
+
+   *opt = 0;
+
+   f = sscanf(str, " %i %n", &v, &n);
+
+   if (f == 1)
+   {
+      *val = v;
+      *opt = CMD_NUMERIC;
+      return n;
+   }
+
+   if (flags & PARSE_FLAGS_PARAMS)
+   {
+      f = sscanf(str, " p%i %n", &v, &n);
+
+      if (f == 1)
+      {
+         *val = v;
+         *opt = CMD_PARAM;
+         return n;
+      }
+   }
+
+   if (flags & PARSE_FLAGS_VARS)
+   {
+      f = sscanf(str, " v%i %n", &v, &n);
+
+      if (f == 1)
+      {
+         *val = v;
+         *opt = CMD_VAR;
+         return n;
+      }
+   }
+
+   return 0;
+}
+
+static char intCmdStr[32];
+
+char *cmdStr(void)
+{
+   return intCmdStr;
+}
+
+int cmdParse(char *buf, uint32_t *p, void **v, gpioCtlParse_t *ctl)
+{
+   int f, valid, idx, val, pp, pars, n, n2, i;
    char *ptr;
-   char c, t;
+   char c;
+   int param[MAX_PARAM];
+
+   bzero(&ctl->opt, sizeof(ctl->opt));
 
-   sscanf(buf, " %7s", str);
+   sscanf(buf+ctl->eaten, " %31s", intCmdStr);
 
-   cmd->cmd = -1;
+   p[0] = -1;
 
-   idx = cmdMatch(str);
+   idx = cmdMatch(intCmdStr);
 
    if (idx < 0) return idx;
 
+   sscanf(buf+ctl->eaten, " %*31s %n", &pp);
+
+   ctl->eaten += pp;
+
    valid = 0;
 
-   cmd->cmd = cmdInfo[idx].cmd;
-   cmd->p1  = 0;
-   cmd->p2  = 0;
+   p[0] = cmdInfo[idx].cmd;
+   p[1]  = 0;
+   p[2]  = 0;
 
    switch (cmdInfo[idx].vt)
    {
-      case 1: /* BR1  BR2  HWVER NO  PIGPV  TICK  WVBSY  WVCLR  WVGO
+      case 1: /* BR1  BR2  HWVER  NO  PIGPV  TICK  WVBSY  WVCLR  WVGO
                  WVGOR  WVHLT
               */
-         f = sscanf(buf, " %7s %c", str, &t);
-         if (f == 1) valid = 1;
+         valid = 1;
          break;
 
-      case 2: /* MODEG  NC  NP  PFG  PRG  PROCD  PROCR  PROCS  PRRG
+      case 2: /* MODEG  NC  NP  PFG  PRG  PROCD  PROCS  PRRG
                  SLRC  READ  WVSC  WVSM  WVSP
               */
-         f = sscanf(buf, " %7s %d %c", str, &cmd->p1, &t);
-         if (f == 2) valid = 1;
+         ctl->eaten += getNum(buf+ctl->eaten, &p[1], &ctl->opt[0], ctl->flags);
+         if (ctl->opt[0]) valid = 1;
          break;
 
       case 3: /* PFS  PRS  PWM  SERVO  SLR  SLRO  WDOG  WRITE
               */
-         f = sscanf(buf, " %7s %d %d %c", str, &cmd->p1, &cmd->p2, &t);
-         if (f == 3) valid = 1;
+         ctl->eaten += getNum(buf+ctl->eaten, &p[1], &ctl->opt[0], ctl->flags);
+         ctl->eaten += getNum(buf+ctl->eaten, &p[2], &ctl->opt[1], ctl->flags);
+         if (ctl->opt[0] && ctl->opt[1]) valid = 1;
          break;
 
       case 4: /* NB
               */
-         f = sscanf(buf, " %7s %d %x %c", str, &cmd->p1, &cmd->p2, &t);
-         if (f == 3) valid = 1;
+         ctl->eaten += getNum(buf+ctl->eaten, &p[1], &ctl->opt[0], ctl->flags);
+         ctl->eaten += getNum(buf+ctl->eaten, &p[2], &ctl->opt[1], ctl->flags);
+         if (ctl->opt[0] && ctl->opt[1]) valid = 1;
          break;
 
       case 5: /* TRIG
               */
-         f = sscanf(buf, " %7s %d %d %d %c",
-            str, &cmd->p1, &cmd->p2, &ext[0].data, &t);
-         if (f == 4)
-         {
-            ext[0].size = sizeof(unsigned);
-            ext[0].ptr = &ext[0].data;
-            valid = 1;
-         }
+         ctl->eaten += getNum(buf+ctl->eaten, &p[1], &ctl->opt[0], ctl->flags);
+         ctl->eaten += getNum(buf+ctl->eaten, &p[2], &ctl->opt[1], ctl->flags);
+         ctl->eaten += getNum(buf+ctl->eaten, &p[3], &ctl->opt[2], ctl->flags);
+         if (ctl->opt[0] && ctl->opt[1] && ctl->opt[2]) valid = 1;
          break;
 
       case 6: /* HELP
@@ -332,21 +465,23 @@ int cmdParse(char *buf, cmdCmd_t *cmd, int argc, char *argv[], gpioExtent_t *ext
 
       case 7: /* BC1  BC2  BS1  BS2
               */
-         f = sscanf(buf, " %7s %x %c", str, &cmd->p1, &t);
-         if (f == 2) valid = 1;
+         ctl->eaten += getNum(buf+ctl->eaten, &p[1], &ctl->opt[0], ctl->flags);
+         if (ctl->opt[0]) valid = 1;
          break;
 
       case 8: /* MODES
               */
-         f = sscanf(buf, " %7s %d %c %c", str, &cmd->p1, &c, &t);
-         if (f == 3)
+         ctl->eaten += getNum(buf+ctl->eaten, &p[1], &ctl->opt[0], ctl->flags);
+         f = sscanf(buf+ctl->eaten, " %c %n", &c, &n);
+         if (ctl->opt[0] && (f >= 1))
          {
+            ctl->eaten += n;
             val = toupper(c);
             ptr = strchr(fmtMdeStr, val);
             if (ptr != NULL)
             {
                val = ptr - fmtMdeStr;
-               cmd->p2 = val;
+               p[2] = val;
                valid = 1;
             }
          }
@@ -354,15 +489,17 @@ int cmdParse(char *buf, cmdCmd_t *cmd, int argc, char *argv[], gpioExtent_t *ext
 
       case 9: /* PUD
               */
-         f = sscanf(buf, " %7s %d %c %c", str, &cmd->p1, &c, &t);
-         if (f == 3)
+         ctl->eaten += getNum(buf+ctl->eaten, &p[1], &ctl->opt[0], ctl->flags);
+         f = sscanf(buf+ctl->eaten, " %c %n", &c, &n);
+         if (ctl->opt[0] && (f >= 1))
          {
+            ctl->eaten += n;
             val = toupper(c);
             ptr = strchr(fmtPudStr, val);
             if (ptr != NULL)
             {
                val = ptr - fmtPudStr;
-               cmd->p2 = val;
+               p[2] = val;
                valid = 1;
             }
          }
@@ -370,53 +507,169 @@ int cmdParse(char *buf, cmdCmd_t *cmd, int argc, char *argv[], gpioExtent_t *ext
 
       case 10: /* PROC
                */
-         if ((argc == 0) || (argc == 3))
+         p[1] = strlen(buf+ctl->eaten);
+         v[1] = buf+ctl->eaten;
+         ctl->eaten += p[1];
+         valid = 1;
+         break;
+
+      case 11: /* WVAS
+               */
+         ctl->eaten += getNum(buf+ctl->eaten, &p[1], &ctl->opt[0], ctl->flags);
+         ctl->eaten += getNum(buf+ctl->eaten, &p[2], &ctl->opt[1], ctl->flags);
+         ctl->eaten += getNum(buf+ctl->eaten, &p[3], &ctl->opt[2], ctl->flags);
+
+         if (ctl->opt[0] && ctl->opt[1] && ctl->opt[2])
          {
-            if (argc == 3)
-            {
-               cmd->p1 = strlen(argv[2]);
-               ext[0].ptr = argv[2];
-            }
-            else /* pipe i/f */
-            {
-               sscanf(buf, "%*s %n", &p);
-               cmd->p1 = strlen(buf+p);
-               ext[0].ptr = buf+p;
-            }
-            ext[0].size = cmd->p1;
+            p[4] = strlen(buf+ctl->eaten);
+            v[1] = buf+ctl->eaten;
+            ctl->eaten += p[4];
             valid = 1;
          }
          break;
 
-      case 11: /* WVAS
+      case 12: /* PROCR
+               */
+         ctl->eaten += getNum(buf+ctl->eaten, &p[1], &ctl->opt[0], 0);
+
+         pars = 0;
+
+         while (pars < 10)
+         {
+            ctl->eaten += getNum(buf+ctl->eaten, &p[2], &ctl->opt[1], 0);
+            if (ctl->opt[1]) param[pars++] = p[2];
+            else break;
+         }
+
+         p[2] = pars;
+
+         v[1] = param;
+
+         if (ctl->opt[0]) valid = 1;
+         break;
+
+      case 13: /* WVAG
                */
-         if ((argc == 0) || (argc == 6))
+         pars = 0;
+
+         while (pars < MAX_PARAM)
+         {
+            ctl->eaten += getNum(buf+ctl->eaten, &p[1], &ctl->opt[0], 0);
+            if (ctl->opt[0]) param[pars++] = p[1];
+            else break;
+         }
+
+         p[1] = pars/3;
+
+         v[1] = param;
+
+         if (pars && ((pars%3)==0)) valid = 1;
+         break;
+
+      case 20: /*
+              */
+         f = sscanf(buf+ctl->eaten, " %d %n", &p[1], &n);
+         if ((f >= 1) && (p[1] >= 0) && (p[1] < MAX_SCRIPT_VARS))
+         {
+            ctl->eaten += n;
+            valid = 1;
+         }
+         break;
+
+      case 21: /*
+              */
+         f = sscanf(buf+ctl->eaten, " %d %n", &p[1], &n);
+         if (f == 1)
+         {
+            ctl->eaten += n;
+            valid = 1;
+         }
+         break;
+
+      case 22: /*
+              */
+         f = sscanf(buf+ctl->eaten, " %d %n", &p[1], &n);
+         if (f == 1)
          {
-            f = sscanf(buf, " %*s %d %d %d %n",
-               &cmd->p1, &ext[0].data, &ext[1].data, &p);
-            if (f == 3)
+            ctl->eaten += n;
+            valid = 1;
+         }
+         break;
+
+      case 23: /*
+              */
+         valid = 1;
+         break;
+
+      case 24: /*
+              */
+         f = sscanf(buf+ctl->eaten, " %d %d %n", &p[1], &p[2], &n);
+         if ((f >= 2) &&
+             (p[1] >= 0) && (p[1] < MAX_SCRIPT_VARS) &&
+             (p[2] >= 0) && (p[2] < MAX_SCRIPT_VARS))
+         {
+            ctl->eaten += n;
+            valid = 1;
+         }
+         break;
+
+      case 25: /*
+              */
+         f = sscanf(buf+ctl->eaten, " %d %n", &p[1], &n);
+         if ((f >= 1) && (p[1] >= 0) && (p[1] < MAX_SCRIPT_PARAMS))
+         {
+            ctl->eaten += n;
+            valid = 1;
+         }
+         break;
+
+      case 26: /*
+              */
+         f = sscanf(buf+ctl->eaten, " %d %n", &p[1], &n);
+         if (f >= 1)
+         {
+            ctl->eaten += n;
+            valid = 1;
+         }
+         break;
+
+      case 27: /*
+              */
+         f = sscanf(buf+ctl->eaten, " %*s%n %n", &n, &n2);
+         if ((f >= 0) && n)
+         {
+            valid = 1;
+
+            for (i=0; i<n; i++)
             {
-               ext[0].size = sizeof(unsigned);
-               ext[0].ptr = &ext[0].data;
-               ext[1].size = sizeof(unsigned);
-               ext[1].ptr = &ext[1].data;
+               c = buf[ctl->eaten+i];
 
-               if (argc) /* pigs */
-               {
-                  cmd->p2 = strlen(argv[5]);
-                  ext[2].ptr = argv[5];
-               }
-               else /* pipe i/f */
+               if ((!isalnum(c)) && (c != '_') && (c != '-'))
                {
-                  cmd->p2 = strlen(buf+p);
-                  ext[2].ptr = buf+p;
+                  valid = 0;
+                  break;
                }
+            }
 
-               ext[2].size = cmd->p2;
-               valid = 1;
+            if (valid)
+            {
+               p[1] = n;
+               ctl->opt[0] = CMD_NUMERIC;
+               v[1]=buf+ctl->eaten;
+               ctl->eaten += n2;
             }
          }
          break;
+
+      case 28: /*
+              */
+         f = sscanf(buf+ctl->eaten, " %d %d %n", &p[1], &p[2], &n);
+         if ((f >= 2) && (p[1] >= 0) && (p[1] < MAX_SCRIPT_VARS))
+         {
+            ctl->eaten += n;
+            valid = 1;
+         }
+         break;
    }
 
    if (valid) return idx; else return -1;
@@ -432,3 +685,4 @@ char * cmdErrStr(int error)
    }
    return "unknown error";
 }
+
index bdffefb7394c24b19897e60d0dd2cfad945f3c56..7e624059d719aa07e0b6d884c0bd5a6dc957a772 100644 (file)
--- a/command.h
+++ b/command.h
@@ -26,7 +26,7 @@ For more information, please refer to <http://unlicense.org/>
 */
 
 /*
-This version is for pigpio version 11+
+This version is for pigpio version 13+
 */
 
 #ifndef COMMAND_H
@@ -37,23 +37,39 @@ This version is for pigpio version 11+
 
 #include "pigpio.h"
 
+#define MAX_PARAM 512
+
+#define PARSE_FLAGS_PARAMS 1
+#define PARSE_FLAGS_VARS   2
+
+#define CMD_NUMERIC 1
+#define CMD_PARAM   2
+#define CMD_VAR     3
+
 typedef struct
 {
-   int    cmd;  /* command number            */
-   char * name; /* command name              */
-   int    vt;   /* command verification type */
-   int    rv;   /* command return value type */
-   int    ext;  /* command has extensions    */
+   int     flags;
+   int     eaten;
+   uint8_t opt[8];
+} gpioCtlParse_t;
+
+typedef struct
+{
+   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[];
 
-extern char * cmdUsage;
+extern char *cmdUsage;
+
+int cmdParse(char *buf, uint32_t *p, void **v, gpioCtlParse_t *ctlParse);
 
-int cmdParse
-   (char *buf, cmdCmd_t *cmd, int argc, char *argv[], gpioExtent_t * ext);
+char *cmdErrStr(int error);
 
-char * cmdErrStr(int error);
+char *cmdStr(void);
 
 #endif
 
index cde0e58b9b270308ec2503c071454dafa97c0cb4..b9cdb2965e76a1fe21cb558d373a7d86687f69a2 100644 (file)
--- a/pigpio.c
+++ b/pigpio.c
@@ -25,7 +25,7 @@ OTHER DEALINGS IN THE SOFTWARE.
 For more information, please refer to <http://unlicense.org/>
 */
 
-/* pigpio version 12 */
+/* pigpio version 13 */
 
 #include <stdio.h>
 #include <string.h>
@@ -254,13 +254,6 @@ 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                                                              \
    {                                                               \
@@ -491,20 +484,20 @@ bit 0 READ_LAST_NOT_SET_ERROR
 #define TIMED_DMA(x)  (DMA_DEST_DREQ | DMA_PERIPHERAL_MAPPING(x))
 
 #define DBG_MIN_LEVEL 0
+#define DBG_ALWAYS    0
 #define DBG_STARTUP   1
 #define DBG_DMACBS    2
-#define DBG_USER      3
-#define DBG_INTERNAL  4
-#define DBG_SLOW_TICK 5
-#define DBG_FAST_TICK 6
-#define DBG_MAX_LEVEL 6
+#define DBG_SCRIPT    3
+#define DBG_USER      4
+#define DBG_INTERNAL  5
+#define DBG_SLOW_TICK 6
+#define DBG_FAST_TICK 7
+#define DBG_MAX_LEVEL 8
 
 #define GPIO_UNDEFINED 0
-#define GPIO_INPUT     1
-#define GPIO_OUTPUT    2
-#define GPIO_PWM       3
-#define GPIO_SERVO     4
-#define GPIO_ALTERNATE 5
+#define GPIO_WRITE     1
+#define GPIO_PWM       2
+#define GPIO_SERVO     3
 
 #define STACK_SIZE (256*1024)
 
@@ -515,16 +508,17 @@ bit 0 READ_LAST_NOT_SET_ERROR
 #define CYCLES_PER_BLOCK 80
 #define PULSE_PER_CYCLE  25
 
-#define PAGES_PER_BLOCK   53
+#define PAGES_PER_BLOCK 53
 
-#define CBS_PER_IPAGE    117
-#define LVS_PER_IPAGE     38
-#define OFF_PER_IPAGE     38
-#define TCK_PER_IPAGE      2
-#define ON_PER_IPAGE       2
+#define CBS_PER_IPAGE 117
+#define LVS_PER_IPAGE  38
+#define OFF_PER_IPAGE  38
+#define TCK_PER_IPAGE   2
+#define ON_PER_IPAGE    2
+#define PAD_PER_IPAGE   7
 
-#define CBS_PER_OPAGE    118
-#define ONOFF_PER_OPAGE   79
+#define CBS_PER_OPAGE 118
+#define OOL_PER_OPAGE  79
 
 #define CBS_PER_CYCLE ((PULSE_PER_CYCLE*3)+2)
 
@@ -535,11 +529,13 @@ bit 0 READ_LAST_NOT_SET_ERROR
 
 #define BLOCK_SIZE (PAGES_PER_BLOCK*PAGE_SIZE)
 
-#define DMA_PAGES (PAGES_PER_BLOCK * bufferBlocks)
+#define DMAI_PAGES (PAGES_PER_BLOCK * bufferBlocks)
 
-#define TICKSLOTS 50
+#define DMAO_PAGES (PAGES_PER_BLOCK * PI_WAVE_BLOCKS)
 
-#define PI_NOTIFY_SLOTS  32
+#define NUM_OOL (DMAO_PAGES * OOL_PER_OPAGE)
+
+#define TICKSLOTS 50
 
 #define PI_NOTIFY_CLOSED  0
 #define PI_NOTIFY_CLOSING 1
@@ -549,7 +545,7 @@ bit 0 READ_LAST_NOT_SET_ERROR
 
 #define PI_WFRX_NONE   0
 #define PI_WFRX_SERIAL 1
-#define PI_WF_MICROS   2
+#define PI_WF_MICROS   1
 
 #define DATUMS 2000
 
@@ -558,7 +554,7 @@ bit 0 READ_LAST_NOT_SET_ERROR
 #define MAX_EMITS (PIPE_BUF / sizeof(gpioReport_t))
 
 #define SRX_BUF_SIZE 8192
-#define CMD_BUF_SIZE 2048
+#define CMD_BUF_SIZE 4096
 
 /* --------------------------------------------------------------- */
 
@@ -587,13 +583,13 @@ typedef struct
    uint32_t tick         [TCK_PER_IPAGE];
    uint32_t gpioOn       [ON_PER_IPAGE];
    uint32_t periphData;
-   uint32_t pad[7];
+   uint32_t pad          [PAD_PER_IPAGE];
 } dmaIPage_t;
 
 typedef struct
 {
-   dmaCbs_t cb           [CBS_PER_OPAGE];
-   uint32_t gpioOnOff    [ONOFF_PER_OPAGE];
+   dmaCbs_t cb     [CBS_PER_OPAGE];
+   uint32_t OOL    [OOL_PER_OPAGE];
    uint32_t periphData;
 } dmaOPage_t;
 
@@ -630,6 +626,12 @@ typedef struct
    uint32_t bits;
 } gpioGetSamples_t;
 
+typedef struct
+{
+   uint32_t label;
+   int      step;
+} gpioLabelStep_t;
+
 typedef struct
 {
    callbk_t        func;
@@ -642,6 +644,51 @@ typedef struct
    pthread_t       pthId;
 } gpioTimer_t;
 
+#define PI_SCRIPT_FREE     0
+#define PI_SCRIPT_RESERVED 1
+#define PI_SCRIPT_IN_USE   2
+#define PI_SCRIPT_DYING    3
+
+#define PI_SCRIPT_HALT   0
+#define PI_SCRIPT_RUN    1
+#define PI_SCRIPT_DELETE 2
+
+#define PI_SCRIPT_SLOTS 32
+
+#define PI_SCRIPT_STACK_SIZE 256
+
+typedef struct
+{
+   uint32_t p[6];
+   uint8_t opt[8];
+} gpioInstr_t;
+
+typedef struct
+{
+   unsigned id;
+   unsigned state;
+   unsigned request;
+   unsigned run_state;
+   uint32_t waitBits;
+   uint32_t changedBits;
+   pthread_t *pthIdp;
+   pthread_mutex_t pthMutex;
+   pthread_cond_t pthCond;
+/*
+  +-----------+---------+---------+----------------+
+  | PARAMS... | VARS... | CMDS... | STRING AREA... |
+  +-----------+---------+---------+----------------+
+*/
+   int *param;
+   int *var;
+   gpioInstr_t *instr;
+   int instrs;
+   char *str_area;
+   int str_area_len;
+   int str_area_pos;
+} gpioScript_t;
+
+
 typedef struct
 {
    uint16_t valid;
@@ -752,7 +799,7 @@ static struct timespec libStarted;
 
 static uint64_t gpioMask;
 
-static gpioPulse_t wf[3][PI_WAVE_MAX_PULSES];
+static gpioWave_t wf[3][PI_WAVE_MAX_PULSES];
 
 static int wfc[3]={0, 0, 0};
 
@@ -762,14 +809,15 @@ static wfStats_t wfStats=
 {
    0, 0, PI_WAVE_MAX_MICROS,
    0, 0, PI_WAVE_MAX_PULSES,
-   0, 0, (PAGES_PER_BLOCK * CBS_PER_OPAGE)
+   0, 0, (DMAO_PAGES * CBS_PER_OPAGE)
 };
 
 static volatile wfRx_t wfRx[PI_MAX_USER_GPIO+1];
 
-static volatile uint32_t alertBits    = 0;
-static volatile uint32_t monitorBits  = 0;
-static volatile uint32_t notifyBits   = 0;
+static volatile uint32_t alertBits   = 0;
+static volatile uint32_t monitorBits = 0;
+static volatile uint32_t notifyBits  = 0;
+static volatile uint32_t scriptBits  = 0;
 
 static volatile int DMAstarted = 0;
 
@@ -787,6 +835,8 @@ static gpioInfo_t       gpioInfo   [PI_MAX_USER_GPIO+1];
 
 static gpioNotify_t     gpioNotify [PI_NOTIFY_SLOTS];
 
+static gpioScript_t     gpioScript [PI_SCRIPT_SLOTS];
+
 static gpioSignal_t     gpioSignal [PI_MAX_SIGNUM+1];
 
 static gpioTimer_t      gpioTimer  [PI_MAX_TIMER+1];
@@ -864,10 +914,15 @@ static const uint16_t pwmRealRange[PWM_FREQS]=
  
 /* ======================================================================= */
 
-static void intNotifyBits(void);
+/* Internal prototypes.
+*/
 
+static void intNotifyBits(void);
+static void intScriptBits(void);
 static int  gpioNotifyOpenInBand(int fd);
 
+/* ======================================================================= */
+
 static void myGpioSleep(int seconds, int micros)
 {
    struct timespec ts, rem;
@@ -878,11 +933,12 @@ static void myGpioSleep(int seconds, int micros)
    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;
+      ts = rem;
    }
 }
 
+/* ----------------------------------------------------------------------- */
+
 static uint32_t myGpioDelay(uint32_t micros)
 {
    uint32_t start;
@@ -896,6 +952,8 @@ static uint32_t myGpioDelay(uint32_t micros)
    return (systReg[SYST_CLO] - start);
 }
 
+/* ----------------------------------------------------------------------- */
+
 static char * myTimeStamp()
 {
    static struct timeval last;
@@ -926,7 +984,7 @@ static void myCreatePipe(char * name, int perm)
 
    if (chmod(name, perm) < 0)
    {
-      DBG(DBG_MIN_LEVEL, "Can't set permissions (%d) for %s, %m", perm, name);
+      DBG(DBG_ALWAYS, "Can't set permissions (%d) for %s, %m", perm, name);
       return;
    }
 }
@@ -985,30 +1043,176 @@ static uint32_t myGetTick(int pos)
 
 /* ----------------------------------------------------------------------- */
 
-static void myDoCommand
-   (cmdCmd_t *cmd, gpioExtent_t *iExt, gpioExtent_t *oExt)
+static int myParseScript(char *script, gpioScript_t *s)
+{
+   int idx, len, b, i, j, labels, resolved, pos;
+   int status;
+   uint32_t p[10];
+   void *v[10];
+   gpioInstr_t instr;
+   gpioCtlParse_t ctl;
+
+   ctl.flags = PARSE_FLAGS_PARAMS | PARSE_FLAGS_VARS;
+   ctl.eaten = 0;
+
+   status = 0;
+
+   gpioLabelStep_t label_step[MAX_SCRIPT_LABELS];
+
+   len = strlen(script);
+
+   /* calloc space for PARAMS, VARS, CMDS, and STRINGS */
+
+   b = (sizeof(int) * (MAX_SCRIPT_PARAMS + MAX_SCRIPT_VARS)) +
+       (sizeof(gpioInstr_t) * (len + 2) / 2) + len;
+
+   s->param = calloc(b, 1);
+
+   if (s->param == NULL) return -1;
+
+   s->var = s->param + MAX_SCRIPT_PARAMS;
+
+   s->instr = (gpioInstr_t *)(s->var + MAX_SCRIPT_VARS);
+
+   s->str_area = (char *)(s->instr + ((len + 2) / 2));
+
+   s->str_area_len = len;
+   s->str_area_pos = 0;
+
+   s->instrs = 0;
+
+   labels = 0;
+
+   idx = 0;
+
+   while (!status && ((ctl.eaten)<len) && (idx >= 0))
+   {
+      pos = ctl.eaten;
+
+      idx = cmdParse(script, p, v, &ctl);
+
+      memcpy(&instr.p, p, sizeof(instr.p));
+
+      if (idx >= 0)
+      {
+         switch (instr.p[0])
+         {
+            case PI_CMD_LABEL:
+
+               if (labels < MAX_SCRIPT_LABELS)
+               {
+                  /* check label not already used */
+                  for (j=0; j<labels; j++)
+                  {
+                     if (label_step[j].label == instr.p[1])
+                     {
+                        DBG(DBG_USER, "duplicate label: %s", script+pos);
+                        status = PI_DUP_LABEL;
+                        idx = -1;
+                     }
+                  }
+
+                  label_step[labels].label = instr.p[1];
+                  label_step[labels].step = s->instrs;
+                  labels++;
+               }
+               else
+               {
+                  DBG(DBG_USER, "too many labels: %s", script+pos);
+                  status = PI_TOO_MANY_LABELS;
+                  idx = -1;
+               }
+
+               break;
+
+            case PI_CMD_SYS:
+
+               strncpy(s->str_area+s->str_area_pos, v[1], p[1]);
+               s->str_area[s->str_area_pos+p[1]] = 0;
+               instr.p[1] = (int) s->str_area+s->str_area_pos;
+               s->str_area_pos += (p[1] + 1);
+
+               break;
+
+            case PI_CMD_TRIG:
+               break;
+
+         }
+      }
+      else
+      {
+         DBG(DBG_USER, "invalid command: %s", script+pos);
+         status = PI_BAD_SCRIPT_CMD;
+      }
+
+      if (idx >= 0)
+      {
+         if (instr.p[0] != PI_CMD_LABEL)
+         {
+            memcpy(instr.opt, &ctl.opt, sizeof(instr.opt));
+            s->instr[s->instrs++] = instr;
+         }
+      }
+   }
+
+   DBG(DBG_SCRIPT, "status=%d eaten=%d len=%d idx=%d",
+      status, ctl.eaten, len, idx);
+
+   for (i=0; i<s->instrs; i++)
+   {
+      instr = s->instr[i];
+
+      /* resolve jumps */
+
+      if ((instr.p[0] == PI_CMD_JMP) || (instr.p[0] == PI_CMD_CALL) ||
+          (instr.p[0] == PI_CMD_JZ)  || (instr.p[0] == PI_CMD_JNZ)  ||
+          (instr.p[0] == PI_CMD_JM)  || (instr.p[0] == PI_CMD_JP))
+      {
+         resolved = 0;
+
+         for (j=0; j<labels; j++)
+         {
+            if (instr.p[1] == label_step[j].label)
+            {
+               s->instr[i].p[1] = label_step[j].step;
+               resolved = 1;
+               break;
+            }
+         }
+
+         if (!resolved)
+         {
+            DBG(DBG_USER, "can't resolve label %d\n", instr.p[1]);
+            status = PI_BAD_SCRIPT;
+         }
+      }
+   }
+   return status;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int myDoCommand(uint32_t *p, gpioExtent_t *oExt)
 {
-   int p1, p2, res, i;
-   uint32_t mask, tmp;
+   int res, i, tmp;
+   uint32_t mask;
    gpioPulse_t *pulse;
    int masked;
 
-   p1  = cmd->p1;
-   p2  = cmd->p2;
-   res = cmd->res;
+   res = 0;
 
-   switch (cmd->cmd)
+   switch (p[0])
    {
       case PI_CMD_BC1:
          mask = gpioMask;
 
-         res = gpioWrite_Bits_0_31_Clear(p1&mask);
+         res = gpioWrite_Bits_0_31_Clear(p[1]&mask);
 
-         if ((mask | p1) != mask)
+         if ((mask | p[1]) != mask)
          {
-            PERM_ERROR(
+            DBG(DBG_USER, 
                "gpioWrite_Bits_0_31_Clear: bad levels %08X (permissions %08X)",
-               p1, mask);
+               p[1], mask);
             res = PI_SOME_PERMITTED;
          }
          break;
@@ -1016,13 +1220,13 @@ static void myDoCommand
       case PI_CMD_BC2:
          mask = gpioMask>>32;
 
-         res = gpioWrite_Bits_32_53_Clear(p1&mask);
+         res = gpioWrite_Bits_32_53_Clear(p[1]&mask);
 
-         if ((mask | p1) != mask)
+         if ((mask | p[1]) != mask)
          {
-            PERM_ERROR(
+            DBG(DBG_USER, 
                "gpioWrite_Bits_32_53_Clear: bad levels %08X (permissions %08X)",
-               p1, mask);
+               p[1], mask);
             res = PI_SOME_PERMITTED;
          }
          break;
@@ -1034,13 +1238,13 @@ static void myDoCommand
       case PI_CMD_BS1:
          mask = gpioMask;
 
-         res = gpioWrite_Bits_0_31_Set(p1&mask);
+         res = gpioWrite_Bits_0_31_Set(p[1]&mask);
 
-         if ((mask | p1) != mask)
+         if ((mask | p[1]) != mask)
          {
-            PERM_ERROR(
+            DBG(DBG_USER, 
                "gpioWrite_Bits_0_31_Set: bad levels %08X (permissions %08X)",
-               p1, mask);
+               p[1], mask);
             res = PI_SOME_PERMITTED;
          }
          break;
@@ -1048,13 +1252,13 @@ static void myDoCommand
       case PI_CMD_BS2:
          mask = gpioMask>>32;
 
-         res = gpioWrite_Bits_32_53_Set(p1&mask);
+         res = gpioWrite_Bits_32_53_Set(p[1]&mask);
 
-         if ((mask | p1) != mask)
+         if ((mask | p[1]) != mask)
          {
-            PERM_ERROR(
+            DBG(DBG_USER, 
                "gpioWrite_Bits_32_53_Set: bad levels %08X (permissions %08X)",
-               p1, mask);
+               p[1], mask);
             res = PI_SOME_PERMITTED;
          }
          break;
@@ -1063,121 +1267,129 @@ static void myDoCommand
 
       case PI_CMD_HWVER: res = gpioHardwareRevision(); break;
 
-      case PI_CMD_MODEG: res = gpioGetMode(p1); break;
+      case PI_CMD_MICRO: myGpioDelay(p[1]); break;
+
+      case PI_CMD_MILLI: myGpioDelay(p[1] * 1000); break;
+
+      case PI_CMD_MODEG: res = gpioGetMode(p[1]); break;
 
       case PI_CMD_MODES:
-         if (gpioMask & (uint64_t)(1<<p1)) res = gpioSetMode(p1, p2);
+         if (gpioMask & (uint64_t)(1<<p[1])) res = gpioSetMode(p[1], p[2]);
          else
          {
-            PERM_ERROR("gpioSetMode: gpio %d, no permission to update", p1);
+            DBG(DBG_USER, "gpioSetMode: gpio %d, no permission to update", p[1]);
             res = PI_NOT_PERMITTED;
          }
          break;
 
-      case PI_CMD_NB: res = gpioNotifyBegin(p1, p2); break;
+      case PI_CMD_NB: res = gpioNotifyBegin(p[1], p[2]); break;
 
-      case PI_CMD_NC: res = gpioNotifyClose(p1); break;
+      case PI_CMD_NC: res = gpioNotifyClose(p[1]); break;
 
       case PI_CMD_NO: res = gpioNotifyOpen();  break;
 
-      case PI_CMD_NP: res = gpioNotifyPause(p1); break;
+      case PI_CMD_NP: res = gpioNotifyPause(p[1]); break;
 
-      case PI_CMD_PFG: res = gpioGetPWMfrequency(p1); break;
+      case PI_CMD_PFG: res = gpioGetPWMfrequency(p[1]); break;
 
       case PI_CMD_PFS:
-         if (gpioMask & (uint64_t)(1<<p1)) res = gpioSetPWMfrequency(p1, p2);
+         if (gpioMask & (uint64_t)(1<<p[1])) res = gpioSetPWMfrequency(p[1], p[2]);
          else
          {
-            PERM_ERROR(
-               "gpioSetPWMfrequency: gpio %d, no permission to update", p1);
+            DBG(DBG_USER, 
+               "gpioSetPWMfrequency: gpio %d, no permission to update", p[1]);
             res = PI_NOT_PERMITTED;
          }
          break;
 
       case PI_CMD_PIGPV: res = gpioVersion(); break;
 
-      case PI_CMD_PRG: res = gpioGetPWMrange(p1); break;
+      case PI_CMD_PRG: res = gpioGetPWMrange(p[1]); break;
 
       case PI_CMD_PROC:
-         res = gpioStoreScript(iExt[0].ptr);
+         res = gpioStoreScript((char *)p[2]);
          break;
 
-      case PI_CMD_PROCD: res = gpioDeleteScript(p1); break;
+      case PI_CMD_PROCD: res = gpioDeleteScript(p[1]); break;
 
-      case PI_CMD_PROCR: res = gpioRunScript(p1); break;
+      case PI_CMD_PROCP: res = gpioScriptStatus(p[1], oExt[0].ptr); break;
+
+      case PI_CMD_PROCR:
+         res = gpioRunScript(p[1], p[2], (uint32_t *)p[3]);
+         break;
 
-      case PI_CMD_PROCS: res = gpioStopScript(p1); break;
+      case PI_CMD_PROCS: res = gpioStopScript(p[1]); break;
 
-      case PI_CMD_PRRG: res = gpioGetPWMrealRange(p1); break;
+      case PI_CMD_PRRG: res = gpioGetPWMrealRange(p[1]); break;
 
       case PI_CMD_PRS:
-         if (gpioMask & (uint64_t)(1<<p1)) res = gpioSetPWMrange(p1, p2);
+         if (gpioMask & (uint64_t)(1<<p[1])) res = gpioSetPWMrange(p[1], p[2]);
          else
          {
-            PERM_ERROR(
-               "gpioSetPWMrange: gpio %d, no permission to update", p1);
+            DBG(DBG_USER, 
+               "gpioSetPWMrange: gpio %d, no permission to update", p[1]);
             res = PI_NOT_PERMITTED;
          }
          break;
 
       case PI_CMD_PUD:
-         if (gpioMask & (uint64_t)(1<<p1)) res = gpioSetPullUpDown(p1, p2);
+         if (gpioMask & (uint64_t)(1<<p[1])) res = gpioSetPullUpDown(p[1], p[2]);
          else
          {
-            PERM_ERROR(
-               "gpioSetPullUpDown: gpio %d, no permission to update", p1);
+            DBG(DBG_USER, 
+               "gpioSetPullUpDown: gpio %d, no permission to update", p[1]);
             res = PI_NOT_PERMITTED;
          }
          break;
 
       case PI_CMD_PWM:
-         if (gpioMask & (uint64_t)(1<<p1)) res = gpioPWM(p1, p2);
+         if (gpioMask & (uint64_t)(1<<p[1])) res = gpioPWM(p[1], p[2]);
          else
          {
-            PERM_ERROR("gpioPWM: gpio %d, no permission to update", p1);
+            DBG(DBG_USER, "gpioPWM: gpio %d, no permission to update", p[1]);
             res = PI_NOT_PERMITTED;
          }
          break;
 
-      case PI_CMD_READ: res = gpioRead(p1); break;
+      case PI_CMD_READ: res = gpioRead(p[1]); break;
 
       case PI_CMD_SERVO:
-         if (gpioMask & (uint64_t)(1<<p1)) res = gpioServo(p1, p2);
+         if (gpioMask & (uint64_t)(1<<p[1])) res = gpioServo(p[1], p[2]);
          else
          {
-            PERM_ERROR("gpioServo: gpio %d, no permission to update", p1);
+            DBG(DBG_USER, "gpioServo: gpio %d, no permission to update", p[1]);
             res = PI_NOT_PERMITTED;
          }
          break;
 
-      case PI_CMD_SLRO: res = gpioSerialReadOpen(p1, p2); break;
+      case PI_CMD_SLRO: res = gpioSerialReadOpen(p[1], p[2]); break;
 
       case PI_CMD_SLR:
-         if (p2 < oExt[0].size) oExt[0].size = p2;
-         res = gpioSerialRead(p1, oExt[0].ptr, oExt[0].size);
+         if (p[2] < oExt[0].size) oExt[0].size = p[2];
+         res = gpioSerialRead(p[1], oExt[0].ptr, oExt[0].size);
          break;
 
-      case PI_CMD_SLRC: res = gpioSerialReadClose(p1); break;
+      case PI_CMD_SLRC: res = gpioSerialReadClose(p[1]); break;
 
       case PI_CMD_TICK: res = gpioTick(); break;
 
       case PI_CMD_TRIG:
-         if (gpioMask & (uint64_t)(1<<p1))
-            res = gpioTrigger(p1, p2, *(int *) (iExt[0].ptr));
+         if (gpioMask & (uint64_t)(1<<p[1]))
+            res = gpioTrigger(p[1], p[2], p[3]);
          else
          {
-            PERM_ERROR("gpioTrigger: gpio %d, no permission to update", p1);
+            DBG(DBG_USER, "gpioTrigger: gpio %d, no permission to update", p[1]);
             res = PI_NOT_PERMITTED;
          }
          break;
 
-      case PI_CMD_WDOG: res = gpioSetWatchdog(p1, p2); break;
+      case PI_CMD_WDOG: res = gpioSetWatchdog(p[1], p[2]); break;
 
       case PI_CMD_WRITE:
-         if (gpioMask & (uint64_t)(1<<p1)) res = gpioWrite(p1, p2);
+         if (gpioMask & (uint64_t)(1<<p[1])) res = gpioWrite(p[1], p[2]);
          else
          {
-            PERM_ERROR("gpioWrite: gpio %d, no permission to update", p1);
+            DBG(DBG_USER, "gpioWrite: gpio %d, no permission to update", p[1]);
             res = PI_NOT_PERMITTED;
          }
          break;
@@ -1187,10 +1399,10 @@ static void myDoCommand
          /* need to mask off any non permitted gpios */
 
          mask = gpioMask;
-         pulse = iExt[0].ptr;
+         pulse = (gpioPulse_t *)p[2];
          masked = 0;
 
-         for (i=0; i<p1; i++)
+         for (i=0; i<p[1]; i++)
          {
             tmp = pulse[i].gpioOn & mask;
             if (tmp != pulse[i].gpioOn)
@@ -1205,9 +1417,11 @@ static void myDoCommand
                pulse[i].gpioOff = tmp;
                masked = 1;
             }
+            DBG(DBG_SCRIPT, "on=%X off=%X delay=%d",
+               pulse[i].gpioOn, pulse[i].gpioOff, pulse[i].usDelay);
          }
 
-         res = gpioWaveAddGeneric(p1, pulse);
+         res = gpioWaveAddGeneric(p[1], pulse);
 
          /* report permission error unless another error occurred */
          if (masked && (res >= 0)) res = PI_SOME_PERMITTED;
@@ -1215,17 +1429,13 @@ static void myDoCommand
          break;
 
       case PI_CMD_WVAS:
-         if (gpioMask & (uint64_t)(1<<p1))
-            res = gpioWaveAddSerial
-               (p1,
-                *(int *)(iExt[0].ptr),
-                *(int *)(iExt[1].ptr),
-                p2,
-                iExt[2].ptr);
+         if (gpioMask & (uint64_t)(1<<p[1]))
+            res = gpioWaveAddSerial(p[1], p[2], p[3], p[4], (char *)p[5]);
          else
          {
-            PERM_ERROR
-               ("gpioWaveAddSerial: gpio %d, no permission to update", p1);
+            DBG(
+               DBG_USER,
+               "gpioWaveAddSerial: gpio %d, no permission to update", p[1]);
             res = PI_NOT_PERMITTED;
          }
          break;
@@ -1241,7 +1451,7 @@ static void myDoCommand
       case PI_CMD_WVHLT: res = gpioWaveTxStop(); break;
 
       case PI_CMD_WVSC:
-         switch(p1)
+         switch(p[1])
          {
             case 0: res = gpioWaveGetCbs();     break;
             case 1: res = gpioWaveGetHighCbs(); break;
@@ -1251,7 +1461,7 @@ static void myDoCommand
          break;
 
       case PI_CMD_WVSM:
-         switch(p1)
+         switch(p[1])
          {
             case 0: res = gpioWaveGetMicros();     break;
             case 1: res = gpioWaveGetHighMicros(); break;
@@ -1261,7 +1471,7 @@ static void myDoCommand
          break;
 
       case PI_CMD_WVSP:
-         switch(p1)
+         switch(p[1])
          {
             case 0: res = gpioWaveGetPulses();     break;
             case 1: res = gpioWaveGetHighPulses(); break;
@@ -1270,7 +1480,7 @@ static void myDoCommand
          }
          break;
    }
-   cmd->res = res;
+   return (res);
 }
 
 /* ----------------------------------------------------------------------- */
@@ -1390,8 +1600,8 @@ static void myGpioSetServo(unsigned gpio, int oldVal, int newVal)
 
    switchGpioOff = 0;
 
-   realRange = pwmRealRange[gpioInfo[gpio].freqIdx];
-   cycles    = pwmCycles   [gpioInfo[gpio].freqIdx];
+   realRange = pwmRealRange[clkCfg[gpioCfg.clockMicros].servoIdx];
+   cycles    = pwmCycles   [clkCfg[gpioCfg.clockMicros].servoIdx];
 
    newOff = (newVal * realRange)/20000;
    oldOff = (oldVal * realRange)/20000;
@@ -1462,17 +1672,35 @@ static uint32_t waveCbPOadr(int pos)
    return (uint32_t) &dmaOPhys[page]->cb[slot];
 }
 
+/* ----------------------------------------------------------------------- */
+
+static void waveOOLPageSlot(int pos, int *page, int *slot)
+{
+   *page = pos/OOL_PER_OPAGE;
+   *slot = pos%OOL_PER_OPAGE;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+static void waveSetOOL(int pos, uint32_t OOL)
+{
+   int page, slot;
+
+   waveOOLPageSlot(pos, &page, &slot);
+
+   dmaOVirt[page]->OOL[slot] = OOL;
+}
 
 /* ----------------------------------------------------------------------- */
 
-static uint32_t waveOnOffPOadr(int pos)
+static uint32_t waveOOLPOadr(int pos)
 {
    int page, slot;
 
-   page = pos/ONOFF_PER_OPAGE;
-   slot = pos%ONOFF_PER_OPAGE;
+   waveOOLPageSlot(pos, &page, &slot);
 
-   return (uint32_t) &dmaOPhys[page]->gpioOnOff[slot];
+   return (uint32_t) &dmaOPhys[page]->OOL[slot];
 }
 
 
@@ -1491,7 +1719,7 @@ static void waveCbOPrint(int pos)
 
 /* ----------------------------------------------------------------------- */
 
-void waveBitDelay(unsigned baud, unsigned * bitDelay)
+static void waveBitDelay(unsigned baud, unsigned * bitDelay)
 {
    unsigned fullBit, halfBit, s, e, d, m, i, err;
 
@@ -1528,47 +1756,22 @@ void waveBitDelay(unsigned baud, unsigned * bitDelay)
    bitDelay[9] = (e-s)/100;
 }
 
-/* ----------------------------------------------------------------------- */
-
-void gpioWaveDump(void)
-{
-   int i;
-
-   unsigned numPulses, t;
-
-   gpioPulse_t * pulses;
-
-   numPulses = wfc[wfcur];
-   pulses    = wf [wfcur];
-
-   t = 0;
-
-   for (i=0; i<numPulses; i++)
-   {
-      printf("%10u %08X %08X %10u\n",
-         t, pulses[i].gpioOn, pulses[i].gpioOff, pulses[i].usDelay);
-      t += pulses[i].usDelay;
-   }
-}
-
-
-
 /* ----------------------------------------------------------------------- */
 
 static int wave2Cbs(unsigned mode)
 {
-   int cb=0, onoff=0;
+   int cb=0, onoff=0, level=NUM_OOL;
 
-   dmaCbs_t * p=NULL;
+   dmaCbs_t *p=NULL;
 
    unsigned i, half, repeatCb;
 
-   unsigned numPulses;
+   unsigned numWaves;
 
-   gpioPulse_t * pulses;
+   gpioWave_t * waves;
 
-   numPulses = wfc[wfcur];
-   pulses    = wf [wfcur];
+   numWaves = wfc[wfcur];
+   waves    = wf [wfcur];
 
    half = PI_WF_MICROS/2;
 
@@ -1601,37 +1804,57 @@ static int wave2Cbs(unsigned mode)
 
    repeatCb = cb;
 
-   for (i=0; i<numPulses; i++)
+   for (i=0; i<numWaves; i++)
    {
-      if (pulses[i].gpioOn)
+      if (waves[i].gpioOn)
       {
-         dmaOVirt[onoff/ONOFF_PER_OPAGE]->gpioOnOff[onoff%ONOFF_PER_OPAGE] =
-            pulses[i].gpioOn;
+         waveSetOOL(onoff, waves[i].gpioOn);
 
          p = waveCbVOadr(cb++);
 
          p->info   = NORMAL_DMA;
-         p->src    = waveOnOffPOadr(onoff++) | DMA_BUS_ADR;
+         p->src    = waveOOLPOadr(onoff++) | DMA_BUS_ADR;
          p->dst    = ((GPIO_BASE + (GPSET0*4)) & 0x00ffffff) | 0x7e000000;
          p->length = 4;
          p->next   = waveCbPOadr(cb) | DMA_BUS_ADR;
       }
 
-      if (pulses[i].gpioOff)
+      if (waves[i].gpioOff)
       {
-         dmaOVirt[onoff/ONOFF_PER_OPAGE]->gpioOnOff[onoff%ONOFF_PER_OPAGE] =
-            pulses[i].gpioOff;
+         waveSetOOL(onoff, waves[i].gpioOff);
 
          p = waveCbVOadr(cb++);
 
          p->info   = NORMAL_DMA;
-         p->src    = waveOnOffPOadr(onoff++) | DMA_BUS_ADR;
+         p->src    = waveOOLPOadr(onoff++) | DMA_BUS_ADR;
          p->dst    = ((GPIO_BASE + (GPCLR0*4)) & 0x00ffffff) | 0x7e000000;
          p->length = 4;
          p->next   = waveCbPOadr(cb) | DMA_BUS_ADR;
       }
 
-      if (pulses[i].usDelay)
+      if (waves[i].flags & WAVE_FLAG_READ)
+      {
+         p = waveCbVOadr(cb++);
+
+         p->info   = NORMAL_DMA;
+         p->src    = ((GPIO_BASE + (GPLEV0*4)) & 0x00ffffff) | 0x7e000000;
+         p->dst    = waveOOLPOadr(--level) | DMA_BUS_ADR;
+         p->length = 4;
+         p->next   = waveCbPOadr(cb) | DMA_BUS_ADR;
+      }
+
+      if (waves[i].flags & WAVE_FLAG_TICK)
+      {
+         p = waveCbVOadr(cb++);
+
+         p->info   = NORMAL_DMA;
+         p->src    = ((SYST_BASE + (SYST_CLO*4)) & 0x00ffffff) | 0x7e000000;
+         p->dst    = waveOOLPOadr(--level) | DMA_BUS_ADR;
+         p->length = 4;
+         p->next   = waveCbPOadr(cb) | DMA_BUS_ADR;
+      }
+
+      if (waves[i].usDelay)
       {
          p = waveCbVOadr(cb++);
 
@@ -1655,7 +1878,7 @@ static int wave2Cbs(unsigned mode)
          }
 
          p->src    = (uint32_t) (&dmaOPhys[0]->periphData) | DMA_BUS_ADR;
-         p->length = 4 * ((pulses[i].usDelay+half)/PI_WF_MICROS);
+         p->length = 4 * ((waves[i].usDelay+half)/PI_WF_MICROS);
          p->next   = waveCbPOadr(cb) | DMA_BUS_ADR;
       }
    }
@@ -1755,9 +1978,9 @@ static void waveRxBit(int gpio, int level, uint32_t tick)
 
 /* ----------------------------------------------------------------------- */
 
-static int waveMerge(unsigned numIn1, gpioPulse_t * in1)
+static int waveMerge(unsigned numIn1, gpioWave_t *in1)
 {
-   unsigned inPos1=0, inPos2=0, outPos=0;
+   unsigned inPos1=0, inPos2=0, outPos=0, level = NUM_OOL;
 
    unsigned cbs=0;
 
@@ -1765,7 +1988,7 @@ static int waveMerge(unsigned numIn1, gpioPulse_t * in1)
 
    uint32_t tNow, tNext1, tNext2, tDelay;
 
-   gpioPulse_t * in2, * out;
+   gpioWave_t *in2, *out;
 
    numIn2 = wfc[wfcur];
    in2    = wf[wfcur];
@@ -1793,6 +2016,7 @@ static int waveMerge(unsigned numIn1, gpioPulse_t * in1)
 
          out[outPos].gpioOn  = in1[inPos1].gpioOn;
          out[outPos].gpioOff = in1[inPos1].gpioOff;
+         out[outPos].flags   = in1[inPos1].flags;
 
          tNext1 = tNow + in1[inPos1].usDelay; ++inPos1;
       }
@@ -1809,6 +2033,7 @@ static int waveMerge(unsigned numIn1, gpioPulse_t * in1)
 
          out[outPos].gpioOn  = in2[inPos2].gpioOn;
          out[outPos].gpioOff = in2[inPos2].gpioOff;
+         out[outPos].flags   = in2[inPos2].flags;
 
          tNext2 = tNow + in2[inPos2].usDelay; ++inPos2;
       }
@@ -1825,6 +2050,7 @@ static int waveMerge(unsigned numIn1, gpioPulse_t * in1)
 
          out[outPos].gpioOn  = in1[inPos1].gpioOn  | in2[inPos2].gpioOn;
          out[outPos].gpioOff = in1[inPos1].gpioOff | in2[inPos2].gpioOff;
+         out[outPos].flags   = in1[inPos1].flags   | in2[inPos2].flags;
 
          tNext1 = tNow + in1[inPos1].usDelay; ++inPos1;
          tNext2 = tNow + in2[inPos2].usDelay; ++inPos2;
@@ -1841,6 +2067,18 @@ static int waveMerge(unsigned numIn1, gpioPulse_t * in1)
 
       if (out[outPos].gpioOff) cbs++; /* one cb if gpio off */
 
+      if (out[outPos].flags & WAVE_FLAG_READ)
+      {
+         cbs++; /* one cb if read */
+         --level;
+      }
+
+      if (out[outPos].flags & WAVE_FLAG_TICK)
+      {
+         cbs++; /* one cb if tick */
+         --level;
+      }
+
       outPos++;
 
       if (inPos1 >= numIn1) tNext1 = -1;
@@ -1848,7 +2086,7 @@ static int waveMerge(unsigned numIn1, gpioPulse_t * in1)
 
    }
 
-   if (outPos < numOut)
+   if ((outPos < numOut) && (outPos < level))
    {
       wfStats.micros = tNow;
 
@@ -1897,7 +2135,7 @@ static void dmaCbPrint(int pos)
 
 /* ----------------------------------------------------------------------- */
 
-static unsigned dmaCurrentCb(void)
+static unsigned dmaNowAtICB(void)
 {
    unsigned cb;
    static unsigned lastPage=0;
@@ -1932,7 +2170,43 @@ static unsigned dmaCurrentCb(void)
          return (page*CBS_PER_IPAGE) + cb;
       }
 
-      if (page++ >= DMA_PAGES) page=0;
+      if (page++ >= DMAI_PAGES) page=0;
+
+      if (page == lastPage) break;
+   }
+
+   return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+unsigned dmaNowAtOCB(void)
+{
+   unsigned cb;
+   static unsigned lastPage=0;
+   unsigned page;
+   uint32_t cbAddr;
+
+   cbAddr = dmaOut[DMA_CONBLK_AD];
+
+   if (!cbAddr) return -1;
+
+   page = lastPage;
+
+   /* which page are we dma'ing? */
+
+   while (1)
+   {
+      cb = (cbAddr - ((int)dmaOPhys[page] | DMA_BUS_ADR)) / 32;
+
+      if (cb < CBS_PER_OPAGE)
+      {
+         lastPage = page;
+
+         return (page*CBS_PER_OPAGE) + cb;
+      }
+
+      if (page++ >= DMAO_PAGES) page=0;
 
       if (page == lastPage) break;
    }
@@ -1961,6 +2235,8 @@ static uint32_t dmaPwmDataAdr(int pos)
    return (uint32_t) &dmaIPhys[pos]->periphData;
 }
 
+/* ----------------------------------------------------------------------- */
+
 static uint32_t dmaGpioOnAdr(int pos)
 {
    int page, slot;
@@ -2095,7 +2371,7 @@ static void dmaDelayCb(int b)
       p->dst    = ((PWM_BASE + PWM_FIFO*4) & 0x00ffffff) | 0x7e000000;
    }
 
-   p->src    = dmaPwmDataAdr(b%DMA_PAGES) | DMA_BUS_ADR;
+   p->src    = dmaPwmDataAdr(b%DMAI_PAGES) | DMA_BUS_ADR;
    p->length = 4;
    p->next   = dmaCbAdr(b+1) | DMA_BUS_ADR;
 }
@@ -2186,13 +2462,19 @@ static void sigHandler(int signum)
          }
          else if (signum == SIGPIPE)
          {
+            /* can happen when pipe/socket is remote closed */
             DBG(DBG_USER, "SIGPIPE received");
          }
+         else if (signum == SIGCHLD)
+         {
+            /* happens when system call is made */
+            DBG(DBG_USER, "SIGCHLD received");
+         }
          else
          {
             /* exit */
 
-            DBG(DBG_MIN_LEVEL, "Unhandled signal %d, terminating\n", signum);
+            DBG(DBG_ALWAYS, "Unhandled signal %d, terminating\n", signum);
 
             exit(-1);
          }
@@ -2202,7 +2484,7 @@ static void sigHandler(int signum)
    {
       /* exit */
 
-      DBG(DBG_MIN_LEVEL, "Unhandled signal %d, terminating\n", signum);
+      DBG(DBG_ALWAYS, "Unhandled signal %d, terminating\n", signum);
 
       exit(-1);
    }
@@ -2256,7 +2538,7 @@ static void * pthAlertThread(void *x)
 
    gpioStats.startTick = tick;
 
-   oldSlot = dmaCurrentSlot(dmaCurrentCb());
+   oldSlot = dmaCurrentSlot(dmaNowAtICB());
 
    cycle = (oldSlot/PULSE_PER_CYCLE);
    pulse = (oldSlot%PULSE_PER_CYCLE);
@@ -2273,7 +2555,7 @@ static void * pthAlertThread(void *x)
          req.tv_nsec = rem.tv_nsec;
       }
 
-      newSlot = dmaCurrentSlot(dmaCurrentCb());
+      newSlot = dmaCurrentSlot(dmaNowAtICB());
 
       numSamples = 0;
 
@@ -2581,6 +2863,28 @@ static void * pthAlertThread(void *x)
          }
       }
 
+      if (changedBits & scriptBits)
+      {
+         for (n=0; n<PI_SCRIPT_SLOTS; n++)
+         {
+            if ((gpioScript[n].state     == PI_SCRIPT_IN_USE)  &&
+                (gpioScript[n].run_state == PI_SCRIPT_WAITING) &&
+                (gpioScript[n].waitBits & changedBits))
+            {
+               pthread_mutex_lock(&gpioScript[n].pthMutex);
+
+               if (gpioScript[n].run_state == PI_SCRIPT_WAITING)
+               {
+                  gpioScript[n].changedBits =
+                     gpioScript[n].waitBits & changedBits;
+                  pthread_cond_signal(&gpioScript[n].pthCond);
+               }
+
+               pthread_mutex_unlock(&gpioScript[n].pthMutex);
+            }
+         }
+      }
+
       /* once all outputs have been emitted set reported level */
 
       if (numSamples) reportedLevel = gpioSample[numSamples-1].level;
@@ -2594,34 +2898,288 @@ static void * pthAlertThread(void *x)
    return 0;
 }
 
+/* ======================================================================= */
+
+static int scrPop(gpioScript_t *s, int *SP, int *S)
+{
+   if ((*SP) > 0)
+   {
+      return S[--(*SP)];
+   }
+   else
+   {
+      s->run_state = PI_SCRIPT_FAILED;
+      DBG(DBG_ALWAYS, "script %d too many pops", s->id);
+      return 0;
+   }
+}
+
 /* ----------------------------------------------------------------------- */
 
-static void * pthTimerTick(void *x)
+static void scrPush(gpioScript_t *s, int *SP, int *S, int val)
 {
-   gpioTimer_t *     tp;
-   struct timespec   req, rem, period;
-   char              buf[256];
+   if ((*SP) < PI_SCRIPT_STACK_SIZE)
+   {
+      S[(*SP)++] = val;
+   }
+   else
+   {
+      s->run_state = PI_SCRIPT_FAILED;
+      DBG(DBG_ALWAYS, "script %d too many pushes", s->id);
+   }
+}
 
-   tp = x;
+/* ----------------------------------------------------------------------- */
 
-   clock_gettime(CLOCK_REALTIME, &tp->nextTick);
+static void scrSwap(int *v1, int *v2)
+{
+   int t;
 
-   while (1)
+   t=*v1; *v1=*v2; *v2= t;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int scrWait(gpioScript_t *s, uint32_t bits)
+{
+   pthread_mutex_lock(&s->pthMutex);
+
+   if (s->request == PI_SCRIPT_RUN)
    {
-      clock_gettime(CLOCK_REALTIME, &rem);
+      s->run_state = PI_SCRIPT_WAITING;
+      s->waitBits = bits;
+      intScriptBits();
 
-      period.tv_sec  = tp->millis / THOUSAND;
-      period.tv_nsec = (tp->millis % THOUSAND) * THOUSAND * THOUSAND;
+      pthread_cond_wait(&s->pthCond, &s->pthMutex);
 
-      do
-      {
-         TIMER_ADD(&tp->nextTick, &period, &tp->nextTick);
+      s->waitBits = 0;
+      intScriptBits();
+      s->run_state = PI_SCRIPT_RUNNING;
+   }
 
-         TIMER_SUB(&tp->nextTick, &rem, &req);
-      }
-      while (req.tv_sec < 0);
+   pthread_mutex_unlock(&s->pthMutex);
 
-      while (nanosleep(&req, &rem))
+   return s->changedBits;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int scrSys(char *cmd, int param)
+{
+   char buf[256];
+   char par[16];
+
+   sprintf(par, " %d", param);
+   strcpy(buf, "/opt/pigpio/cgi/");
+   strncat(buf, cmd, 200);
+   strcat(buf, par);
+
+   DBG(DBG_USER, "sys %s", buf);
+
+   return system(buf);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void *pthScript(void *x)
+{
+   gpioScript_t *s;
+   gpioExtent_t oExt[3];
+   char buf[CMD_BUF_SIZE];
+   gpioInstr_t instr;
+   int p1, p2;
+
+   int PC, A, F, SP;
+   int S[PI_SCRIPT_STACK_SIZE];
+
+   S[0] = 0; /* to prevent compiler warning */
+
+   s = x;
+
+   s->run_state = PI_SCRIPT_HALTED;
+
+   while (s->request != PI_SCRIPT_DELETE)
+   {
+      pthread_mutex_lock(&s->pthMutex);
+      pthread_cond_wait(&s->pthCond, &s->pthMutex);
+      pthread_mutex_unlock(&s->pthMutex);
+
+      s->run_state = PI_SCRIPT_RUNNING;
+
+      A  = 0;
+      F  = 0;
+      PC = 0;
+      SP = 0;
+
+      while ((s->request   == PI_SCRIPT_RUN    ) &&
+             (s->run_state == PI_SCRIPT_RUNNING))
+      {
+         instr = s->instr[PC];
+
+         p1 = instr.p[1];
+         p2 = instr.p[2];
+
+         if (instr.p[0] < 100)
+         {
+            oExt[0].ptr = buf;
+            oExt[0].size = CMD_BUF_SIZE-1;
+
+            if (instr.opt[0] == CMD_PARAM)
+            {
+               instr.p[1] = s->param[p1];
+            }
+            else if (instr.opt[0] == CMD_VAR)
+            {
+               instr.p[1] = s->var[p1];
+            }
+
+            if (instr.opt[1] == CMD_PARAM)
+            {
+               instr.p[2] = s->param[p2];
+            }
+            else if (instr.opt[1] == CMD_VAR)
+            {
+               instr.p[2] = s->var[p2];
+            }
+
+            A = myDoCommand(instr.p, oExt);
+
+            F = A;
+
+            PC++;
+         }
+         else
+         {
+            switch (instr.p[0])
+            {
+               case PI_CMD_ADDI:  A+=p1; F=A;                      PC++; break;
+
+               case PI_CMD_ADDV:  A+=s->var[p1]; F=A;              PC++; break;
+
+               case PI_CMD_ANDI:  A&=p1; F=A;                      PC++; break;
+
+               case PI_CMD_ANDV:  A&=s->var[p1]; F=A;              PC++; break;
+
+               case PI_CMD_CALL:  scrPush(s, S, &SP, PC+1);     PC = p1; break;
+
+               case PI_CMD_CMPI:  F=A-p1;                          PC++; break;
+
+               case PI_CMD_CMPV:  F=A-s->var[p1];                  PC++; break;
+
+               case PI_CMD_DCRA:  --A; F=A;                        PC++; break;
+
+               case PI_CMD_DCRV:  --s->var[p1]; F=s->var[p1];      PC++; break;
+
+               case PI_CMD_HALT:  s->run_state = PI_SCRIPT_HALTED;       break;
+
+               case PI_CMD_INRA:  ++A; F=A;                        PC++; break;
+
+               case PI_CMD_INRV:  ++s->var[p1]; F=s->var[p1];      PC++; break;
+
+               case PI_CMD_JM:    if (F<0)  PC=p1; else PC++;            break;
+
+               case PI_CMD_JMP:   PC=p1;                                 break;
+
+               case PI_CMD_JNZ:   if (F)    PC=p1; else PC++;            break;
+
+               case PI_CMD_JP:    if (F>=0) PC=p1; else PC++;            break;
+
+               case PI_CMD_JZ:    if (!F)   PC=p1; else PC++;            break;
+
+               case PI_CMD_LABEL:                                  PC++; break;
+
+               case PI_CMD_LDAI:  A=p1;                            PC++; break;
+
+               case PI_CMD_LDAP:  A=s->param[p1];                  PC++; break;
+
+               case PI_CMD_LDAV:  A=s->var[p1];                    PC++; break;
+
+               case PI_CMD_LDPA:  s->param[p1]=A;                  PC++; break;
+
+               case PI_CMD_LDVA:  s->var[p1]=A;                    PC++; break;
+
+               case PI_CMD_LDVI:  s->var[p1] = p2;                 PC++; break;
+
+               case PI_CMD_LDVV:  s->var[p1]=s->var[p2];           PC++; break;
+
+               case PI_CMD_ORI:   A|=p1; F=A;                      PC++; break;
+
+               case PI_CMD_ORV:   A|=s->var[p1]; F=A;              PC++; break;
+
+               case PI_CMD_POPA:  A=scrPop(s, S, &SP);             PC++; break;
+
+               case PI_CMD_POPV:  s->var[p1]=scrPop(s, S, &SP);    PC++; break;
+
+               case PI_CMD_PUSHA: scrPush(s, S, &SP, A);           PC++; break;
+
+               case PI_CMD_PUSHV: scrPush(s, S, &SP, s->var[p1]);  PC++; break;
+
+               case PI_CMD_RET:   PC=scrPop(s, S, &SP);                  break;
+
+               case PI_CMD_RAL:   A<<=p1; F=A;                     PC++; break;
+
+               case PI_CMD_RAR:   A>>=p1; F=A;                     PC++; break;
+
+               case PI_CMD_SUBI:  A-=p1; F=A;                      PC++; break;
+
+               case PI_CMD_SUBV:  A-=s->var[p1]; F=A;              PC++; break;
+
+               case PI_CMD_SWAPA: scrSwap(&s->var[p1], &A);        PC++; break;
+
+               case PI_CMD_SWAPV: scrSwap(&s->var[p1], &s->var[p2]);PC++; break;
+
+               case PI_CMD_SYS:   A=scrSys((char*)p1, A); F=A;     PC++; break;
+
+               case PI_CMD_WAITI: A=scrWait(s, p1); F=A;           PC++; break;
+
+               case PI_CMD_WAITV: A=scrWait(s, s->var[p1]); F=A;   PC++; break;
+
+               case PI_CMD_XORI:  A^=p1; F=A;                      PC++; break;
+
+               case PI_CMD_XORV:  A^=s->var[p1]; F=A;              PC++; break;
+
+            }
+         }
+
+         if (PC >= s->instrs) s->run_state = PI_SCRIPT_HALTED;
+
+      }
+
+      if (s->request == PI_SCRIPT_HALT) s->run_state = PI_SCRIPT_HALTED;
+
+   }
+
+   return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void * pthTimerTick(void *x)
+{
+   gpioTimer_t *     tp;
+   struct timespec   req, rem, period;
+   char              buf[256];
+
+   tp = x;
+
+   clock_gettime(CLOCK_REALTIME, &tp->nextTick);
+
+   while (1)
+   {
+      clock_gettime(CLOCK_REALTIME, &rem);
+
+      period.tv_sec  = tp->millis / THOUSAND;
+      period.tv_nsec = (tp->millis % THOUSAND) * THOUSAND * THOUSAND;
+
+      do
+      {
+         TIMER_ADD(&tp->nextTick, &period, &tp->nextTick);
+
+         TIMER_SUB(&tp->nextTick, &rem, &req);
+      }
+      while (req.tv_sec < 0);
+
+      while (nanosleep(&req, &rem))
       {
          req.tv_sec  = rem.tv_sec;
          req.tv_nsec = rem.tv_nsec;
@@ -2652,11 +3210,15 @@ static void * pthTimerTick(void *x)
 static void * pthFifoThread(void *x)
 {
    char buf[CMD_BUF_SIZE];
-   int idx, flags, len;
-   cmdCmd_t cmd;
-   gpioExtent_t iExt[3];
+   int idx, flags, len, res, i;
+   uint32_t p[10];
+   void *v[10];
    gpioExtent_t oExt[3];
-   char *p;
+   gpioCtlParse_t ctl;
+   char *pp;
+   uint32_t *param;
+
+   ctl.flags = 0;
 
    myCreatePipe(PI_INPFIFO, 0662);
 
@@ -2686,53 +3248,79 @@ static void * pthFifoThread(void *x)
         buf[len] = 0; /* replace terminating */
       }
 
-      if ((idx=cmdParse(buf, &cmd, 0, NULL, iExt)) >= 0)
+      ctl.eaten = 0;
+      idx = 0;
+
+      while (((ctl.eaten)<len) && (idx >= 0))
       {
-         oExt[0].ptr = buf;
-         oExt[0].size = CMD_BUF_SIZE-1;
+         if ((idx=cmdParse(buf, p, v, &ctl)) >= 0)
+         {
+            oExt[0].ptr = buf;
+            oExt[0].size = CMD_BUF_SIZE-1;
 
-         myDoCommand(&cmd, iExt, oExt);
+            switch (p[0])
+            {
+               case PI_CMD_PROC:  p[2] = (uint32_t)v[1]; break;
+               case PI_CMD_PROCR: p[3] = (uint32_t)v[1]; break;
+               case PI_CMD_WVAG:  p[2] = (uint32_t)v[1]; break;
+               case PI_CMD_WVAS:  p[5] = (uint32_t)v[1]; break;
+            }
 
-         switch (cmdInfo[idx].rv)
-         {
-            case 0:
-               fprintf(outFifo, "%d\n", cmd.res);
-               break;
+            res = myDoCommand(p, oExt);
 
-            case 1:
-               fprintf(outFifo, "%d\n", cmd.res);
-               break;
+            switch (cmdInfo[idx].rv)
+            {
+               case 0:
+                  fprintf(outFifo, "%d\n", res);
+                  break;
 
-            case 2:
-               fprintf(outFifo, "%d\n", cmd.res);
-               break;
+               case 1:
+                  fprintf(outFifo, "%d\n", res);
+                  break;
 
-            case 3:
-               fprintf(outFifo, "%08X\n", cmd.res);
-               break;
+               case 2:
+                  fprintf(outFifo, "%d\n", res);
+                  break;
 
-            case 4:
-               fprintf(outFifo, "%u\n", cmd.res);
-               break;
+               case 3:
+                  fprintf(outFifo, "%08X\n", res);
+                  break;
 
-            case 5:
-               fprintf(outFifo, cmdUsage);
-               break;
+               case 4:
+                  fprintf(outFifo, "%u\n", res);
+                  break;
 
-            case 6:
-               if (cmd.res < 0) fprintf(outFifo, "%d\n", cmd.res);
-               else if (cmd.res > 0)
-               {
-                  p = oExt[0].ptr;
-                  p[cmd.res] = 0;
-                  fprintf(outFifo, "%s", (char *)oExt[0].ptr);
-               }
-               break;
+               case 5:
+                  fprintf(outFifo, cmdUsage);
+                  break;
 
-         }
+               case 6:
+                  if (res < 0) fprintf(outFifo, "%d\n", res);
+                  else if (res > 0)
+                  {
+                     pp = oExt[0].ptr;
+                     pp[res] = 0;
+                     fprintf(outFifo, "%s", (char *)oExt[0].ptr);
+                  }
+                  break;
 
+               case 7:
+                  if (res < 0) fprintf(outFifo, "%d\n", res);
+                  else
+                  {
+                     fprintf(outFifo, "%d", res);
+                     param = oExt[0].ptr;
+                     for (i=0; i<MAX_SCRIPT_PARAMS; i++)
+                     {
+                        fprintf(outFifo, " %d", param[i]);
+                     }
+                     fprintf(outFifo, "\n");
+                  }
+                  break;
+            }
+         }
+         else fprintf(outFifo, "%d\n", PI_BAD_FIFO_COMMAND);
       }
-      else fprintf(outFifo, "%d\n", PI_BAD_FIFO_COMMAND);
 
       fflush(outFifo);
    }
@@ -2745,12 +3333,11 @@ static void * pthFifoThread(void *x)
 static void *pthSocketThreadHandler(void *fdC)
 {
    int sock = *(int*)fdC;
-   cmdCmd_t cmd;
+   int res;
+   uint32_t p[10];
    unsigned bytes;
    char *memPtr;
-   gpioExtent_t iExt[3];
    gpioExtent_t oExt[3];
-   unsigned tmp;
    char buf[CMD_BUF_SIZE];
 
    oExt[0].size = CMD_BUF_SIZE-1;
@@ -2758,16 +3345,100 @@ static void *pthSocketThreadHandler(void *fdC)
 
    free(fdC);
 
-   while(1)
+   while (1)
    {
-      if (recv(sock, &cmd, sizeof(cmdCmd_t), MSG_WAITALL) == sizeof(cmdCmd_t))
+      res = 0;
+
+      if (recv(sock, p, 16, MSG_WAITALL) != 16) break;
+
+      switch (p[0])
       {
-         if (cmd.cmd == PI_CMD_NOIB)
-         {
-            cmd.res = gpioNotifyOpenInBand(sock);
-         }
-         else if (cmd.cmd == PI_CMD_WVAG)
-         {
+         case PI_CMD_NOIB:
+            res = gpioNotifyOpenInBand(sock);
+            break;
+
+         case PI_CMD_PROC:
+            /*
+            p1=script length
+            p2=0
+            ## extension ##
+            char[] script
+            */
+            if (p[1]) /* script appended */
+            {
+               memPtr = malloc(p[1]+1); /* add null terminator */
+
+               if (memPtr)
+               {
+                  if (recv(sock, memPtr, p[1], MSG_WAITALL) == p[1])
+                  {
+                     p[2] = (uint32_t)memPtr;
+                     memPtr[p[1]] = 0;
+                     res = myDoCommand(p, oExt);
+                     free(memPtr);
+                  }
+                  else
+                  {
+                     free(memPtr);
+                     close(sock);
+                     return 0;
+                  }
+               }
+            }
+            else res = PI_BAD_SCRIPT_CMD;
+            break;
+
+         case PI_CMD_PROCR:
+            /*
+            p1=script id
+            p2=numParam
+            ## extension ##
+            int[] param
+            */
+            if (p[2]) /* parameters appended */
+            {
+               memPtr = malloc(p[2]*sizeof(uint32_t));
+
+               if (memPtr)
+               {
+                  if (recv(sock, memPtr, p[2]*sizeof(uint32_t), MSG_WAITALL) ==
+                     p[2]*sizeof(uint32_t))
+                  {
+                     p[3] = (uint32_t)memPtr;
+                     res = myDoCommand(p, oExt);
+                     free(memPtr);
+                  }
+                  else
+                  {
+                     free(memPtr);
+                     close(sock);
+                     return 0;
+                  }
+               }
+            }
+            else res = myDoCommand(p, oExt);
+            break;
+
+         case PI_CMD_TRIG:
+            /*
+            p1=user_gpio
+            p2=pulseLen
+            ## extension ##
+            unsigned level
+            */
+            if (recv(sock, &p[3], sizeof(unsigned), MSG_WAITALL) ==
+               sizeof(unsigned))
+            {
+               res = myDoCommand(p, oExt);
+            }
+            else
+            {
+               close(sock);
+               return 0;
+            }
+            break;
+
+         case PI_CMD_WVAG:
             /*
             p1=numPulses
             p2=0
@@ -2775,7 +3446,7 @@ static void *pthSocketThreadHandler(void *fdC)
             gpioPulse_t[] pulses
             */
 
-            bytes = cmd.p1 * sizeof(gpioPulse_t);
+            bytes = p[1] * sizeof(gpioPulse_t);
 
             memPtr = malloc(bytes);
 
@@ -2783,22 +3454,21 @@ static void *pthSocketThreadHandler(void *fdC)
             {
                if (recv(sock, memPtr, bytes, MSG_WAITALL) == bytes)
                {
-                  iExt[0].size = bytes;
-                  iExt[0].ptr = memPtr;
-                  myDoCommand(&cmd, iExt, oExt);
+                  p[2] = (uint32_t)memPtr;
+                  res = myDoCommand(p, oExt);
                   free(memPtr);
                }
                else
                {
                   free(memPtr);
-                  break;
+                  close(sock);
+                  return 0;
                }
             }
-            else break;
+            else res = PI_NO_MEMORY;
+            break;
 
-         }
-         else if (cmd.cmd == PI_CMD_WVAS)
-         {
+         case PI_CMD_WVAS:
             /*
             p1=user_gpio
             p2=numChar
@@ -2808,7 +3478,7 @@ static void *pthSocketThreadHandler(void *fdC)
             char[] str
             */
 
-            bytes = sizeof(unsigned) + sizeof(unsigned) + cmd.p2;
+            bytes = sizeof(unsigned) + sizeof(unsigned) + p[2];
 
             memPtr = malloc(bytes+1); /* add 1 for a nul terminator */
 
@@ -2816,103 +3486,55 @@ static void *pthSocketThreadHandler(void *fdC)
             {
                if (recv(sock, memPtr, bytes, MSG_WAITALL) == bytes)
                {
-                  iExt[0].size = sizeof(unsigned);
-                  iExt[0].ptr = memPtr;
-                  iExt[1].size = sizeof(unsigned);
-                  iExt[1].ptr = memPtr + sizeof(unsigned);
-                  iExt[2].size = cmd.p2;
-                  iExt[2].ptr = memPtr + sizeof(unsigned) + sizeof(unsigned);
+                  p[4] = p[2];
+                  memcpy(&p[2], memPtr, 4);
+                  memcpy(&p[3], memPtr+4, 4);
+                  p[5] =
+                     (uint32_t)(memPtr + sizeof(unsigned) + sizeof(unsigned));
                   memPtr[bytes] = 0; /* may be duplicate terminator */
-                  myDoCommand(&cmd, iExt, oExt);
+                  res = myDoCommand(p, oExt);
                   free(memPtr);
                }
                else
                {
                   free(memPtr);
-                  break;
+                  close(sock);
+                  return 0;
                }
             }
-            else break;
+            else res = PI_NO_MEMORY;
+            break;
 
-         }
-         else if (cmd.cmd == PI_CMD_PROC)
-         {
-            /*
-            p1=script length
-            p2=0
-            ## extension ##
-            char[] script
-            */
+         default:
+            res = myDoCommand(p, oExt);
+            break;
+      }
 
-            bytes = cmd.p1;
+      p[3] = res;
 
-            memPtr = malloc(bytes+1); /* add 1 for a nul terminator */
+      write(sock, p, 16);
 
-            if (memPtr)
-            {
-               if (bytes) /* script appended */
-               {
-                  if (recv(sock, memPtr, bytes, MSG_WAITALL) != bytes)
-                  {
-                     free(memPtr);
-                     break;
-                  }
-               }
-               iExt[0].size = bytes;
-               iExt[0].ptr = memPtr;
-               memPtr[bytes] = 0; /* may be duplicate terminator */
-               myDoCommand(&cmd, iExt, oExt);
-               free(memPtr);
-            }
-            else break;
-         }
-         else
-         {
-            switch (cmd.cmd)
-            {
-               case PI_CMD_TRIG:
-                  /*
-                  p1=user_gpio
-                  p2=pulseLen
-                  ## extension ##
-                  unsigned level
-                  */
-                  iExt[0].size = 4;
-                  iExt[0].ptr = &tmp;
-
-                  if (recv(sock, &tmp, sizeof(unsigned), MSG_WAITALL) !=
-                     sizeof(unsigned))
-                  {
-                     close(sock);
-                     return 0;
-                  }
-                  break;
+      switch (p[0])
+      {
+         case PI_CMD_SLR: /* extension */
 
-               default:
-                  break;
+            if (res > 0)
+            {
+               write(sock, oExt[0].ptr, res);
             }
-            myDoCommand(&cmd, iExt, oExt);
-         }
+            break;
 
-         write(sock, &cmd, sizeof(cmdCmd_t));
-
-         switch (cmd.cmd)
-         {
-            case PI_CMD_SLR: /* extension */
-
-               if (cmd.res > 0)
-               {
-                  write(sock, oExt[0].ptr, cmd.res);
-               }
-               break;
-
-            default:
-               break;
-         }
+         case PI_CMD_PROCP: /* extension */
 
+            if (res >= 0)
+            {
+               write(sock, oExt[0].ptr, sizeof(uint32_t)*MAX_SCRIPT_PARAMS);
+            }
+            break;
 
+         default:
+           break;
       }
-      else break;
    }
 
    close(sock);
@@ -3291,7 +3913,7 @@ static int initDMAcbs(void)
       (uint32_t)gpioReg, (uint32_t)pwmReg,
       (uint32_t)pcmReg,  (uint32_t)clkReg); 
 
-   for (i=0; i<DMA_PAGES; i++)
+   for (i=0; i<DMAI_PAGES; i++)
       DBG(DBG_STARTUP, "dmaIPhys[%d]=%08X", i, (uint32_t)dmaIPhys[i]);
 
    dmaInitCbs();
@@ -3404,6 +4026,7 @@ static void initPCM(unsigned bits)
 
 static void initClock(int mainClock)
 {
+   int clockPWM;
    unsigned clkCtl, clkDiv, clkSrc, clkDivI, clkDivF, clkMash, clkBits;
    char * per, * src;
    unsigned micros;
@@ -3413,18 +4036,20 @@ static void initClock(int mainClock)
    if (mainClock) micros = gpioCfg.clockMicros;
    else           micros = PI_WF_MICROS;
 
-   if ((gpioCfg.clockPeriph == PI_CLOCK_PCM) && mainClock)
-   {
-      clkCtl = CLK_PCMCTL;
-      clkDiv = CLK_PCMDIV;
-      per = "PCM";
-   }
-   else
+   clockPWM = mainClock ^ (gpioCfg.clockPeriph == PI_CLOCK_PCM);
+
+   if (clockPWM)
    {
       clkCtl = CLK_PWMCTL;
       clkDiv = CLK_PWMDIV;
       per = "PWM";
    }
+   else
+   {
+      clkCtl = CLK_PCMCTL;
+      clkDiv = CLK_PCMDIV;
+      per = "PCM";
+   }
 
    if (gpioCfg.clockSource == PI_CLOCK_PLLD)
    {
@@ -3466,16 +4091,15 @@ static void initClock(int mainClock)
 
    myGpioDelay(10);
 
-   if ((gpioCfg.clockPeriph == PI_CLOCK_PCM) && mainClock)
-        initPCM(clkBits);
-   else initPWM(clkBits);
+   if (clockPWM) initPWM(clkBits);
+   else          initPCM(clkBits);
 
    myGpioDelay(2000);
 }
 
 /* ----------------------------------------------------------------------- */
 
-static void initDMAgo(volatile uint32_t  * dmaAddr, uint32_t cbAddr)
+static void initDMAgo(volatile uint32_t  *dmaAddr, uint32_t cbAddr)
 {
    DBG(DBG_STARTUP, "");
 
@@ -3508,9 +4132,10 @@ static void initClearGlobals(void)
 
    DBG(DBG_STARTUP, "");
 
-   alertBits    = 0;
-   monitorBits  = 0;
-   notifyBits   = 0;
+   alertBits   = 0;
+   monitorBits = 0;
+   notifyBits  = 0;
+   scriptBits  = 0;
 
    libInitialised   = 0;
    DMAstarted       = 0;
@@ -3535,7 +4160,7 @@ static void initClearGlobals(void)
 
    wfStats.cbs        = 0;
    wfStats.highCbs    = 0;
-   wfStats.maxCbs     = (PAGES_PER_BLOCK * CBS_PER_OPAGE);
+   wfStats.maxCbs     = (PI_WAVE_BLOCKS * PAGES_PER_BLOCK * CBS_PER_OPAGE);
 
    gpioGetSamples.func     = NULL;
    gpioGetSamples.ex       = 0;
@@ -3646,91 +4271,264 @@ static void initReleaseResources(void)
       pthSocketRunning = 0;
    }
 
-   /* release mmap'd memory */
+   /* release mmap'd memory */
+
+   if (clkReg  != MAP_FAILED) munmap((void *)clkReg,  CLK_LEN);
+   if (dmaReg  != MAP_FAILED) munmap((void *)dmaReg,  DMA_LEN);
+   if (gpioReg != MAP_FAILED) munmap((void *)gpioReg, GPIO_LEN);
+   if (pcmReg  != MAP_FAILED) munmap((void *)pcmReg,  PCM_LEN);
+   if (pwmReg  != MAP_FAILED) munmap((void *)pwmReg,  PWM_LEN);
+   if (systReg != MAP_FAILED) munmap((void *)systReg, SYST_LEN);
+
+   clkReg  = MAP_FAILED;
+   dmaReg  = MAP_FAILED;
+   gpioReg = MAP_FAILED;
+   pcmReg  = MAP_FAILED;
+   pwmReg  = MAP_FAILED;
+   systReg = MAP_FAILED;
+
+   if (dmaVirt != MAP_FAILED)
+   {
+      for (i=0; i<PAGES_PER_BLOCK*(bufferBlocks+PI_WAVE_BLOCKS); i++)
+      {
+         munmap(dmaVirt[i], PAGE_SIZE);
+      }
+
+      munmap(dmaVirt,
+         PAGES_PER_BLOCK*(bufferBlocks+PI_WAVE_BLOCKS)*sizeof(dmaPage_t *));
+   }
+
+   dmaVirt = MAP_FAILED;
+
+   if (dmaPhys != MAP_FAILED)
+   {
+      for (i=0; i<PAGES_PER_BLOCK*(bufferBlocks+PI_WAVE_BLOCKS); i++)
+      {
+         munmap(dmaPhys[i], PAGE_SIZE);
+      }
+
+      munmap(dmaPhys,
+         PAGES_PER_BLOCK*(bufferBlocks+PI_WAVE_BLOCKS)*sizeof(dmaPage_t *));
+   }
+
+   dmaPhys = MAP_FAILED;
+
+   if (dmaBloc != MAP_FAILED)
+   {
+      for (i=0; i<(bufferBlocks+PI_WAVE_BLOCKS); i++)
+      {
+         munmap(dmaBloc[i], PAGES_PER_BLOCK*PAGE_SIZE);
+      }
+
+      munmap(dmaBloc, (bufferBlocks+PI_WAVE_BLOCKS)*sizeof(dmaPage_t *));
+   }
+
+   dmaBloc = MAP_FAILED;
+
+   if (inpFifo != NULL)
+   {
+      fclose(inpFifo);
+      unlink(PI_INPFIFO);
+      inpFifo = NULL;
+   }
+
+   if (outFifo != NULL)
+   {
+      fclose(outFifo);
+      unlink(PI_OUTFIFO);
+      outFifo = NULL;
+   }
+
+   if (fdMem != -1)
+   {
+      close(fdMem);
+      fdMem = -1;
+   }
+
+   if (fdLock != -1)
+   {
+      close(fdLock);
+      unlink(PI_LOCKFILE);
+      fdLock = -1;
+   }
+
+   if (fdSock != -1)
+   {
+      close(fdSock);
+      fdSock = -1;
+   }
+}
+
+/* ======================================================================= */
+
+int getBitInBytes(int bitPos, uint8_t *buf, int numBits)
+{
+   int bitp, bufp;
+
+   if (bitPos < numBits)
+   {
+      bufp =      bitPos / 8;
+      bitp = 7 - (bitPos % 8);
+      if (buf[bufp] & (1<<bitp)) return 1;
+   }
+
+   return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+void putBitInBytes(int bitPos, uint8_t *buf, int val)
+{
+   int bitp, bufp;
+
+   bufp =      bitPos / 8;
+   bitp = 7 - (bitPos % 8);
+
+   if (val) buf[bufp] |=   (1<<bitp);
+   else     buf[bufp] &= (~(1<<bitp));
+}
+
+/* ----------------------------------------------------------------------- */
+
+uint32_t waveGetRawOut(int pos)
+{
+   int page, slot;
+
+   if ((pos >= 0) && (pos < NUM_OOL))
+   {
+      waveOOLPageSlot(pos, &page, &slot);
+      return (dmaOVirt[page]->OOL[slot]);
+   }
+
+   return -1;
+}
+
+/* ----------------------------------------------------------------------- */
+
+void waveSetRawOut(int pos, uint32_t value)
+{
+   int page, slot;
+
+   if ((pos >= 0) && (pos < NUM_OOL))
+   {
+      waveOOLPageSlot(pos, &page, &slot);
+      dmaOVirt[page]->OOL[slot] = value;
+   }
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+uint32_t waveGetRawIn(int pos)
+{
+   int page, slot;
+
+   if ((pos >= 0) && (pos < NUM_OOL))
+   {
+      waveOOLPageSlot((NUM_OOL-1)-pos, &page, &slot);
+      return (dmaOVirt[page]->OOL[slot]);
+   }
+
+   return -1;
+}
+
+/* ----------------------------------------------------------------------- */
+
+void waveSetRawIn(int pos, uint32_t value)
+{
+   int page, slot;
+
+   if ((pos >= 0) && (pos < NUM_OOL))
+   {
+      waveOOLPageSlot((NUM_OOL-1)-pos, &page, &slot);
+      dmaOVirt[page]->OOL[slot] = value;
+   }
+}
+
+/* ----------------------------------------------------------------------- */
+
+double time_time(void)
+{
+   struct timeval tv;
+   double t;
 
-   if (clkReg  != MAP_FAILED) munmap((void *)clkReg,  CLK_LEN);
-   if (dmaReg  != MAP_FAILED) munmap((void *)dmaReg,  DMA_LEN);
-   if (gpioReg != MAP_FAILED) munmap((void *)gpioReg, GPIO_LEN);
-   if (pcmReg  != MAP_FAILED) munmap((void *)pcmReg,  PCM_LEN);
-   if (pwmReg  != MAP_FAILED) munmap((void *)pwmReg,  PWM_LEN);
-   if (systReg != MAP_FAILED) munmap((void *)systReg, SYST_LEN);
+   gettimeofday(&tv, 0);
 
-   clkReg  = MAP_FAILED;
-   dmaReg  = MAP_FAILED;
-   gpioReg = MAP_FAILED;
-   pcmReg  = MAP_FAILED;
-   pwmReg  = MAP_FAILED;
-   systReg = MAP_FAILED;
+   t = (double)tv.tv_sec + ((double)tv.tv_usec / 1E6);
 
-   if (dmaVirt != MAP_FAILED)
-   {
-      for (i=0; i<PAGES_PER_BLOCK*(bufferBlocks+PI_WAVE_BLOCKS); i++)
-      {
-         munmap(dmaVirt[i], PAGE_SIZE);
-      }
+   return t;
+}
 
-      munmap(dmaVirt,
-         PAGES_PER_BLOCK*(bufferBlocks+PI_WAVE_BLOCKS)*sizeof(dmaPage_t *));
-   }
+/* ----------------------------------------------------------------------- */
 
-   dmaVirt = MAP_FAILED;
+void time_sleep(double seconds)
+{
+   struct timespec ts, rem;
 
-   if (dmaPhys != MAP_FAILED)
+   if (seconds > 0.0)
    {
-      for (i=0; i<PAGES_PER_BLOCK*(bufferBlocks+PI_WAVE_BLOCKS); i++)
+      ts.tv_sec = seconds;
+      ts.tv_nsec = (seconds-(double)ts.tv_sec) * 1E9;
+
+      while (clock_nanosleep(CLOCK_REALTIME, 0, &ts, &rem))
       {
-         munmap(dmaPhys[i], PAGE_SIZE);
+         /* copy remaining time to ts */
+         ts.tv_sec  = rem.tv_sec;
+         ts.tv_nsec = rem.tv_nsec;
       }
-
-      munmap(dmaPhys,
-         PAGES_PER_BLOCK*(bufferBlocks+PI_WAVE_BLOCKS)*sizeof(dmaPage_t *));
    }
+}
 
-   dmaPhys = MAP_FAILED;
+/* ----------------------------------------------------------------------- */
 
-   if (dmaBloc != MAP_FAILED)
-   {
-      for (i=0; i<(bufferBlocks+PI_WAVE_BLOCKS); i++)
-      {
-         munmap(dmaBloc[i], PAGES_PER_BLOCK*PAGE_SIZE);
-      }
+void gpioDumpWave(void)
+{
+   int i;
 
-      munmap(dmaBloc, (bufferBlocks+PI_WAVE_BLOCKS)*sizeof(dmaPage_t *));
-   }
+   unsigned numWaves, t;
 
-   dmaBloc = MAP_FAILED;
+   gpioWave_t *waves;
 
-   if (inpFifo != NULL)
-   {
-      fclose(inpFifo);
-      unlink(PI_INPFIFO);
-      inpFifo = NULL;
-   }
+   numWaves = wfc[wfcur];
+   waves    = wf [wfcur];
 
-   if (outFifo != NULL)
+   t = 0;
+
+   for (i=0; i<numWaves; i++)
    {
-      fclose(outFifo);
-      unlink(PI_OUTFIFO);
-      outFifo = NULL;
+      fprintf(stderr, "%10u %08X %08X %08X %10u\n",
+         t, waves[i].gpioOn, waves[i].gpioOff,
+         waves[i].flags, waves[i].usDelay);
+      t += waves[i].usDelay;
    }
+}
 
-   if (fdMem != -1)
+/* ----------------------------------------------------------------------- */
+
+void gpioDumpScript(int s)
+{
+   int i;
+
+   for (i=0; i<MAX_SCRIPT_PARAMS; i++)
    {
-      close(fdMem);
-      fdMem = -1;
+      fprintf(stderr, "p%d=%d ", i, gpioScript[s].param[i]);
    }
 
-   if (fdLock != -1)
+   fprintf(stderr, "\n");
+
+   for (i=0; i<MAX_SCRIPT_VARS; i++)
    {
-      close(fdLock);
-      unlink(PI_LOCKFILE);
-      fdLock = -1;
+      fprintf(stderr, "v%d=%d ", i, gpioScript[s].var[i]);
    }
 
-   if (fdSock != -1)
+   fprintf(stderr, "\n");
+
+   for (i=0; i<gpioScript[s].instrs; i++)
    {
-      close(fdSock);
-      fdSock = -1;
+      fprintf(stderr, "c%d=[%d, %d(%d), %d(%d)]\n",
+         i, gpioScript[s].instr[i].p[0],
+         gpioScript[s].instr[i].p[1], gpioScript[s].instr[i].opt[0],
+         gpioScript[s].instr[i].p[2], gpioScript[s].instr[i].opt[1]);
    }
 }
 
@@ -3920,7 +4718,6 @@ int gpioSetMode(unsigned gpio, unsigned mode)
                /* switch pwm off */
                myGpioSetPwm(gpio, gpioInfo[gpio].width, 0);
                break;
-
          }
 
          gpioInfo[gpio].is = GPIO_UNDEFINED;
@@ -4015,10 +4812,12 @@ int gpioWrite(unsigned gpio, unsigned level)
 
    if (gpio <= PI_MAX_USER_GPIO)
    {
-      if (gpioInfo[gpio].is != GPIO_OUTPUT)
+      if (gpioInfo[gpio].is != GPIO_WRITE)
       {
          if (gpioInfo[gpio].is == GPIO_UNDEFINED)
          {
+            if (level == PI_OFF) *(gpioReg + GPCLR0 + BANK) = BIT;
+            else                 *(gpioReg + GPSET0 + BANK) = BIT;
             gpioSetMode(gpio, PI_OUTPUT);
          }
          else if (gpioInfo[gpio].is == GPIO_PWM)
@@ -4033,7 +4832,7 @@ int gpioWrite(unsigned gpio, unsigned level)
                gpio, gpioInfo[gpio].width/gpioCfg.clockMicros, 0);
          }
 
-         gpioInfo[gpio].is=GPIO_OUTPUT;
+         gpioInfo[gpio].is=GPIO_WRITE;
          gpioInfo[gpio].width=0;
       }
    }
@@ -4070,7 +4869,6 @@ int gpioPWM(unsigned gpio, unsigned val)
          /* switch servo off */
          myGpioSetServo(gpio, gpioInfo[gpio].width, 0);
          gpioInfo[gpio].width = 0;
-         gpioInfo[gpio].freqIdx = DEFAULT_PWM_IDX; /* default frequency */
       }
       gpioInfo[gpio].is = GPIO_PWM;
    }
@@ -4251,7 +5049,6 @@ int gpioServo(unsigned gpio, unsigned val)
          gpioInfo[gpio].width=0;
       }
       gpioInfo[gpio].is = GPIO_SERVO;
-      gpioInfo[gpio].freqIdx = clkCfg[gpioCfg.clockMicros].servoIdx;
    }
 
    myGpioSetServo(gpio, gpioInfo[gpio].width, val);
@@ -4394,8 +5191,10 @@ int gpioWaveClear(void)
 
 /* ----------------------------------------------------------------------- */
 
-int gpioWaveAddGeneric(unsigned numPulses, gpioPulse_t * pulses)
+int gpioWaveAddGeneric(unsigned numPulses, gpioPulse_t *pulses)
 {
+   int p;
+
    DBG(DBG_USER, "numPulses=%u pulses=%08X", numPulses, (uint32_t)pulses);
 
    CHECK_INITED;
@@ -4403,7 +5202,15 @@ int gpioWaveAddGeneric(unsigned numPulses, gpioPulse_t * pulses)
    if (numPulses > PI_WAVE_MAX_PULSES)
       SOFT_ERROR(PI_TOO_MANY_PULSES, "bad number of pulses (%d)", numPulses);
 
-   return waveMerge(numPulses, pulses);
+   for (p=0; p<numPulses; p++)
+   {
+      wf[2][p].gpioOff = pulses[p].gpioOff;
+      wf[2][p].gpioOn  = pulses[p].gpioOn;
+      wf[2][p].usDelay = pulses[p].usDelay;
+      wf[2][p].flags   = 0;
+   }
+
+   return waveMerge(numPulses, wf[2]);
 }
 
 
@@ -4419,7 +5226,7 @@ int gpioWaveAddSerial(unsigned gpio,
 
    unsigned bitDelay[10];
 
-   DBG(DBG_USER, "gpio=%d baud=%d offset=%d numChar=%d str=%s",
+   DBG(DBG_USER, "gpio=%d baud=%d offset=%d numChar=%d str=[%s]",
       gpio, baud, offset, numChar, str);
 
    DBG(DBG_USER, "l=%d s=%X e=%X",
@@ -4448,6 +5255,7 @@ int gpioWaveAddSerial(unsigned gpio,
 
    wf[2][p].gpioOn  = (1<<gpio);
    wf[2][p].gpioOff = 0;
+   wf[2][p].flags   = 0;
 
    if (offset > bitDelay[0]) wf[2][p].usDelay = offset;
    else                      wf[2][p].usDelay = bitDelay[0];
@@ -4461,6 +5269,7 @@ int gpioWaveAddSerial(unsigned gpio,
       wf[2][p].gpioOn = 0;
       wf[2][p].gpioOff = (1<<gpio);
       wf[2][p].usDelay = bitDelay[0];
+      wf[2][p].flags   = 0;
 
       lev = 0;
 
@@ -4479,13 +5288,15 @@ int gpioWaveAddSerial(unsigned gpio,
 
             if (lev)
             {
-               wf[2][p].gpioOn = (1<<gpio);
+               wf[2][p].gpioOn  = (1<<gpio);
                wf[2][p].gpioOff = 0;
+               wf[2][p].flags   = 0;
             }
             else
             {
-               wf[2][p].gpioOn = 0;
+               wf[2][p].gpioOn  = 0;
                wf[2][p].gpioOff = (1<<gpio);
+               wf[2][p].flags   = 0;
             }
 
             wf[2][p].usDelay = bitDelay[b+1];
@@ -4499,9 +5310,10 @@ int gpioWaveAddSerial(unsigned gpio,
       {
          p++;
 
-         wf[2][p].gpioOn = (1<<gpio);
+         wf[2][p].gpioOn  = (1<<gpio);
          wf[2][p].gpioOff = 0;
          wf[2][p].usDelay = bitDelay[9];
+         wf[2][p].flags   = 0;
       }
    }
 
@@ -4510,10 +5322,150 @@ int gpioWaveAddSerial(unsigned gpio,
    wf[2][p].gpioOn  = (1<<gpio);
    wf[2][p].gpioOff = 0;
    wf[2][p].usDelay = bitDelay[0];
+   wf[2][p].flags   = 0;
 
    return waveMerge(p, wf[2]);
 }
 
+/* ----------------------------------------------------------------------- */
+
+int gpioWaveAddSPI(
+   gpioSPI_t *spi,
+   unsigned offset,
+   unsigned ss,
+   uint8_t *tx_bits,
+   unsigned num_tx_bits,
+   unsigned rx_bit_first,
+   unsigned rx_bit_last,
+   unsigned bits)
+{
+   int p, dbv, bit, halfbit;
+   int rising_edge[2], read_cycle[2];
+   uint32_t on_bits, off_bits;
+   int tx_bit_pos;
+
+   DBG(DBG_USER, "spi=%08X off=%d ss=%d tx=%08X, num=%d fb=%d lb=%d bits=%d",
+      (uint32_t)spi, offset, ss, (uint32_t)tx_bits, num_tx_bits,
+      rx_bit_first, rx_bit_last, bits);
+
+   CHECK_INITED;
+
+   if (ss > PI_MAX_USER_GPIO)
+      SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", ss);
+
+   /*
+   CPOL CPHA
+    0    0   read rising/write falling
+    0    1   read falling/write rising
+    1    0   read falling/write rising
+    1    1   read rising/write falling
+   */
+
+   if (spi->clk_pol) {rising_edge[0] = 0; rising_edge[1] = 1;}
+   else              {rising_edge[0] = 1; rising_edge[1] = 0;}
+
+   if (spi->clk_pha) {read_cycle[0] = 0; read_cycle[1] = 1;}
+   else              {read_cycle[0] = 1; read_cycle[1] = 0;}
+
+   p = 0;
+
+   if (offset)
+   {
+      wf[2][p].gpioOn  = 0;
+      wf[2][p].gpioOff = 0;
+      wf[2][p].flags   = 0;
+      wf[2][p].usDelay = offset;
+      p++;
+   }
+
+   /* preset initial mosi bit in case it's read at leading clock bit */
+
+   on_bits = 0;
+   off_bits = 0;
+
+   tx_bit_pos = 0;
+
+   if (getBitInBytes(tx_bit_pos, tx_bits, num_tx_bits))
+   {
+      dbv = 1;
+      on_bits  |= (1<<(spi->mosi));
+   }
+   else
+   {
+      dbv = 0;
+      off_bits  |= (1<<(spi->mosi));
+   }
+
+   if (spi->ss_pol) off_bits |= (1<<ss);
+   else             on_bits  |= (1<<ss);
+
+   wf[2][p].gpioOn  = on_bits;
+   wf[2][p].gpioOff = off_bits;
+   wf[2][p].flags   = 0;
+
+   if (spi->clk_us > spi->ss_us) wf[2][p].usDelay = spi->clk_us;
+   else                          wf[2][p].usDelay = spi->ss_us;
+
+   p++;
+
+   for (bit=1; bit<=bits; bit++)
+   {
+      for (halfbit=0; halfbit<2; halfbit++)
+      {
+         wf[2][p].usDelay = spi->clk_us;
+         wf[2][p].flags = 0;
+
+         on_bits = 0;
+         off_bits = 0;
+
+         if (read_cycle[halfbit])
+         {
+            if ((bit>=rx_bit_first) && (bit<=rx_bit_last))
+               wf[2][p].flags = WAVE_FLAG_READ;
+
+;
+         }
+         else
+         {
+            if (getBitInBytes(tx_bit_pos, tx_bits, num_tx_bits))
+            {
+               if (!dbv) on_bits |= (1<<(spi->mosi));
+               dbv = 1;
+            }
+            else
+            {
+               if (dbv) off_bits |= (1<<(spi->mosi));
+               dbv = 0;
+            }
+
+            ++tx_bit_pos;
+         }
+
+         if (rising_edge[halfbit]) on_bits  |= (1<<(spi->clk));
+         else                      off_bits |= (1<<(spi->clk));
+
+         wf[2][p].gpioOn = on_bits;
+         wf[2][p].gpioOff = off_bits;
+
+         p++;
+      }
+   }
+
+   on_bits = 0;
+   off_bits = 0;
+
+   if (spi->ss_pol) on_bits  |= (1<<ss);
+   else             off_bits |= (1<<ss);
+
+   wf[2][p].gpioOn  = on_bits;
+   wf[2][p].gpioOff = off_bits;
+   wf[2][p].flags   = 0;
+   wf[2][p].usDelay = 0;
+
+   p++;
+
+   return waveMerge(p, wf[2]);
+}
 
 /*-------------------------------------------------------------------------*/
 
@@ -4672,6 +5624,10 @@ int gpioWaveTxStart(unsigned mode)
       secondaryClockInited = 1;
    }
 
+   dmaOut[DMA_CS] = DMA_CHANNEL_RESET;
+
+   dmaOut[DMA_CONBLK_AD] = 0;
+
    cb = wave2Cbs(mode);
 
    if (gpioCfg.dbgLevel >= DBG_SLOW_TICK)
@@ -4727,7 +5683,7 @@ static int intGpioSetAlertFunc(
       alertBits &= ~BIT;
    }
 
-   monitorBits = alertBits | notifyBits | gpioGetSamples.bits;
+   monitorBits = alertBits | notifyBits | scriptBits | gpioGetSamples.bits;
 
    return 0;
 }
@@ -4784,6 +5740,7 @@ int gpioNotifyOpen(void)
    {
       if (gpioNotify[i].state == PI_NOTIFY_CLOSED)
       {
+         gpioNotify[i].state = PI_NOTIFY_OPENED;
          slot = i;
          break;
       }
@@ -4799,9 +5756,11 @@ int gpioNotifyOpen(void)
    fd = open(name, O_RDWR|O_NONBLOCK);
 
    if (fd < 0)
+   {
+      gpioNotify[slot].state = PI_NOTIFY_CLOSED;
       SOFT_ERROR(PI_BAD_PATHNAME, "open %s failed (%m)", name);
+   }
 
-   gpioNotify[slot].state = PI_NOTIFY_OPENED;
    gpioNotify[slot].seqno = 0;
    gpioNotify[slot].bits  = 0;
    gpioNotify[slot].fd    = fd;
@@ -4846,6 +5805,27 @@ static int gpioNotifyOpenInBand(int fd)
 
 /* ----------------------------------------------------------------------- */
 
+static void intScriptBits(void)
+{
+   int i;
+   uint32_t bits;
+
+   bits = 0;
+
+   for (i=0; i<PI_SCRIPT_SLOTS; i++)
+   {
+      if (gpioScript[i].state == PI_SCRIPT_IN_USE)
+      {
+         bits |= gpioScript[i].waitBits;
+      }
+   }
+
+   scriptBits = bits;
+
+   monitorBits = alertBits | notifyBits | scriptBits | gpioGetSamples.bits;
+}
+
+
 static void intNotifyBits(void)
 {
    int i;
@@ -4863,7 +5843,7 @@ static void intNotifyBits(void)
 
    notifyBits = bits;
 
-   monitorBits = alertBits | notifyBits | gpioGetSamples.bits;
+   monitorBits = alertBits | notifyBits | scriptBits | gpioGetSamples.bits;
 }
 
 
@@ -5006,7 +5986,7 @@ int gpioSetGetSamplesFunc(gpioGetSamplesFunc_t f, uint32_t bits)
    if (f) gpioGetSamples.bits = bits;
    else   gpioGetSamples.bits = 0;
 
-   monitorBits = alertBits | notifyBits | gpioGetSamples.bits;
+   monitorBits = alertBits | notifyBits | scriptBits | gpioGetSamples.bits;
 
    return 0;
 }
@@ -5029,7 +6009,7 @@ int gpioSetGetSamplesFuncEx(gpioGetSamplesFuncEx_t f,
    if (f) gpioGetSamples.bits = bits;
    else   gpioGetSamples.bits = 0;
 
-   monitorBits = alertBits | notifyBits | gpioGetSamples.bits;
+   monitorBits = alertBits | notifyBits | scriptBits | gpioGetSamples.bits;
 
    return 0;
 }
@@ -5182,29 +6162,124 @@ void gpioStopThread(pthread_t *pth)
 
 int gpioStoreScript(char *script)
 {
-   DBG(DBG_USER, "script=%s", script);
-   DBG(DBG_USER, "l=%d s=%X e=%X",
-      strlen(script), script[0], script[strlen(script)-1]);
+   gpioScript_t *s;
+   int status, slot, i;
+
+   DBG(DBG_USER, "script=[%s]", script);
 
    CHECK_INITED;
 
-   return PI_BAD_SCRIPT;
-}
+   slot = -1;
+
+   for (i=0; i<PI_SCRIPT_SLOTS; i++)
+   {
+      if (gpioScript[i].state == PI_SCRIPT_FREE)
+      {
+         gpioScript[i].state = PI_SCRIPT_RESERVED;
+         slot = i;
+         break;
+      }
+   }
+
+   if (slot < 0)
+      SOFT_ERROR(PI_NO_SCRIPT_ROOM, "no room for scripts");
+
+   s = &gpioScript[slot];
+
+   status = myParseScript(script, s);
+
+   if (status == 0)
+   {
+      s->request   = PI_SCRIPT_HALT;
+      s->run_state = PI_SCRIPT_HALTED;
+
+      pthread_cond_init(&s->pthCond, NULL);
+      pthread_mutex_init(&s->pthMutex, NULL);
+
+      s->id = slot;
+
+      gpioScript[slot].state = PI_SCRIPT_IN_USE;
+
+      s->pthIdp = gpioStartThread(pthScript, s);
+
+      status = slot;
 
+   }
+   else
+   {
+      if (s->param) free(s->param);
+      s->param = NULL;
+      gpioScript[slot].state = PI_SCRIPT_FREE;
+   }
+
+   return status;
+}
 
 
 /* ----------------------------------------------------------------------- */
 
-int gpioRunScript(int script_id)
+int gpioRunScript(int script_id, unsigned numParam, uint32_t *param)
 {
-   DBG(DBG_USER, "script_id=%d", script_id);
+   int status = 0;
+
+   DBG(DBG_USER, "script_id=%d numParam=%d param=%08X",
+      script_id, numParam, (uint32_t)param);
 
    CHECK_INITED;
 
-   return PI_BAD_SCRIPT_ID;
+   if (numParam > MAX_SCRIPT_PARAMS)
+      SOFT_ERROR(PI_TOO_MANY_PARAM, "bad number of parameters(%d)", numParam);
+
+   if (gpioScript[script_id].state == PI_SCRIPT_IN_USE)
+   {
+      pthread_mutex_lock(&gpioScript[script_id].pthMutex);
+
+      if (gpioScript[script_id].run_state == PI_SCRIPT_HALTED)
+      {
+         if ((numParam > 0) && (param != 0))
+         {
+            memcpy(gpioScript[script_id].param, param,
+               sizeof(uint32_t) * numParam);
+         }
+
+         gpioScript[script_id].request = PI_SCRIPT_RUN;
+
+         pthread_cond_signal(&gpioScript[script_id].pthCond);
+      }
+      else
+      {
+         status = PI_NOT_HALTED;
+      }
+
+      pthread_mutex_unlock(&gpioScript[script_id].pthMutex);
+
+      return status;
+   }
+   else return PI_BAD_SCRIPT_ID;
 }
 
 
+/* ----------------------------------------------------------------------- */
+
+int gpioScriptStatus(int script_id, uint32_t *param)
+{
+   DBG(DBG_USER, "script_id=%d param=%08X", script_id, (uint32_t)param);
+
+   CHECK_INITED;
+
+   if (gpioScript[script_id].state == PI_SCRIPT_IN_USE)
+   {
+      if (param != 0)
+      {
+         memcpy(param, gpioScript[script_id].param,
+            sizeof(uint32_t) * MAX_SCRIPT_PARAMS);
+      }
+
+      return gpioScript[script_id].run_state;
+   }
+   else return PI_BAD_SCRIPT_ID;
+}
+
 
 /* ----------------------------------------------------------------------- */
 
@@ -5214,10 +6289,23 @@ int gpioStopScript(int script_id)
 
    CHECK_INITED;
 
-   return PI_BAD_SCRIPT_ID;
-}
+   if (gpioScript[script_id].state == PI_SCRIPT_IN_USE)
+   {
+      pthread_mutex_lock(&gpioScript[script_id].pthMutex);
+
+      gpioScript[script_id].request = PI_SCRIPT_HALT;
 
+      if (gpioScript[script_id].run_state == PI_SCRIPT_WAITING)
+      {
+         pthread_cond_signal(&gpioScript[script_id].pthCond);
+      }
 
+      pthread_mutex_unlock(&gpioScript[script_id].pthMutex);
+
+      return 0;
+   }
+   else return PI_BAD_SCRIPT_ID;
+}
 
 /* ----------------------------------------------------------------------- */
 
@@ -5227,7 +6315,37 @@ int gpioDeleteScript(int script_id)
 
    CHECK_INITED;
 
-   return PI_BAD_SCRIPT_ID;
+   if (gpioScript[script_id].state == PI_SCRIPT_IN_USE)
+   {
+      gpioScript[script_id].state = PI_SCRIPT_DYING;
+
+      pthread_mutex_lock(&gpioScript[script_id].pthMutex);
+
+      gpioScript[script_id].request = PI_SCRIPT_HALT;
+
+      if (gpioScript[script_id].run_state == PI_SCRIPT_WAITING)
+      {
+         pthread_cond_signal(&gpioScript[script_id].pthCond);
+      }
+
+      pthread_mutex_unlock(&gpioScript[script_id].pthMutex);
+
+      while (gpioScript[script_id].run_state == PI_SCRIPT_RUNNING)
+      {
+         myGpioSleep(0, 5000); /* give script time to halt */
+      }
+
+      gpioStopThread(gpioScript[script_id].pthIdp);
+
+      if (gpioScript[script_id].param) free(gpioScript[script_id].param);
+
+      gpioScript[script_id].param = NULL;
+
+      gpioScript[script_id].state = PI_SCRIPT_FREE;
+
+      return 0;
+   }
+   else return PI_BAD_SCRIPT_ID;
 }
 
 
@@ -5441,7 +6559,7 @@ uint32_t gpioDelay(uint32_t micros)
 
    start = systReg[SYST_CLO];
 
-   if (micros < 100) while ((systReg[SYST_CLO] - start) <= micros) ;
+   if (micros < 101) while ((systReg[SYST_CLO] - start) <= micros) ;
 
    else gpioSleep(PI_TIME_RELATIVE, (micros/MILLION), (micros%MILLION));
 
@@ -5672,7 +6790,7 @@ int gpioCfgInternals(unsigned what, int value)
 
          gpioCfg.showStats = value;
 
-         DBG(DBG_MIN_LEVEL, "showStats is %u", value);
+         DBG(DBG_ALWAYS, "showStats is %u", value);
 
          retVal = 0;
 
@@ -5680,13 +6798,13 @@ int gpioCfgInternals(unsigned what, int value)
 
       case 984762879:
 
-         if (value < DBG_MIN_LEVEL) value = DBG_MIN_LEVEL;
+         if (value < DBG_ALWAYS) value = DBG_ALWAYS;
 
          if (value > DBG_MAX_LEVEL) value = DBG_MAX_LEVEL;
 
          gpioCfg.dbgLevel = value;
 
-         DBG(DBG_MIN_LEVEL, "Debug level is %u", value);
+         DBG(DBG_ALWAYS, "Debug level is %u", value);
 
          retVal = 0;
 
index bf5fa891855e543918a2131c0694cfe78431049e..0ddac321f6b767b6a489273040b9ff8341fa8acf 100644 (file)
--- a/pigpio.h
+++ b/pigpio.h
@@ -26,7 +26,7 @@ For more information, please refer to <http://unlicense.org/>
 */
 
 /*
-This version is for pigpio version 12
+This version is for pigpio version 13
 */
 
 #ifndef PIGPIO_H
@@ -86,7 +86,7 @@ This version is for pigpio version 12
 #include <stdint.h>
 #include <pthread.h>
 
-#define PIGPIO_VERSION 12
+#define PIGPIO_VERSION 13
 
 /*-------------------------------------------------------------------------*/
 
@@ -164,6 +164,7 @@ gpioStopThread             Stop a previously started thread.
 
 gpioStoreScript            Store a script.
 gpioRunScript              Run a stored script.
+gpioScriptStatus           Get script status and parameters.
 gpioStopScript             Stop a running script.
 gpioDeleteScript           Delete a stored script.
 
@@ -217,6 +218,12 @@ gpioCfgSocketPort          Configure socket port.
 extern "C" {
 #endif
 
+typedef struct
+{
+   uint16_t func;
+   uint16_t size;
+} gpioHeader_t;
+
 typedef struct
 {
    uint32_t cmd;
@@ -229,7 +236,7 @@ typedef struct
 {
    size_t size;
    void *ptr;
-   int data;
+   uint32_t data;
 } gpioExtent_t;
 
 typedef struct
@@ -253,6 +260,29 @@ typedef struct
    uint32_t usDelay;
 } gpioPulse_t;
 
+#define WAVE_FLAG_READ 1
+#define WAVE_FLAG_TICK 2
+
+typedef struct
+{
+   uint32_t gpioOn;
+   uint32_t gpioOff;
+   uint32_t usDelay;
+   uint32_t flags;
+} gpioWave_t;
+
+typedef struct
+{
+   int clk;     /* gpio for clock           */
+   int mosi;    /* gpio for MOSI            */
+   int miso;    /* gpio for MISO            */
+   int ss_pol;  /* slave select off state   */
+   int ss_us;   /* delay after slave select */
+   int clk_pol; /* clock off state          */
+   int clk_pha; /* clock phase              */
+   int clk_us;  /* clock micros             */
+} gpioSPI_t;
+
 typedef void (*gpioAlertFunc_t)    (int      gpio,
                                     int      level,
                                     uint32_t tick);
@@ -768,6 +798,8 @@ int gpioNotifyOpen(void);
    handle.
 */
 
+#define PI_NOTIFY_SLOTS  32
+
 
 
 /*-------------------------------------------------------------------------*/
@@ -915,7 +947,7 @@ int gpioWaveAddSerial(unsigned user_gpio,
    the same waveform.
 */
 
-#define PI_WAVE_BLOCKS     3
+#define PI_WAVE_BLOCKS     4
 #define PI_WAVE_MAX_PULSES (PI_WAVE_BLOCKS * 3000)
 #define PI_WAVE_MAX_CHARS  (PI_WAVE_BLOCKS *  256)
 
@@ -924,8 +956,6 @@ int gpioWaveAddSerial(unsigned user_gpio,
 
 #define PI_WAVE_MAX_MICROS (30 * 60 * 1000000) /* half an hour */
 
-
-
 /*-------------------------------------------------------------------------*/
 int gpioWaveTxStart(unsigned mode);
 /*-------------------------------------------------------------------------*/
@@ -1321,16 +1351,51 @@ int gpioStoreScript(char *script);
    otherwise PI_BAD_SCRIPT.
 */
 
+#define MAX_SCRIPT_LABELS  50
+#define MAX_SCRIPT_VARS   150
+#define MAX_SCRIPT_PARAMS  10
 
 
 /* ----------------------------------------------------------------------- */
-int gpioRunScript(int script_id);
+int gpioRunScript(int script_id, unsigned numParam, uint32_t *param);
 /* ----------------------------------------------------------------------- */
 /* This function runs a stored script.
 
-   The function returns 0 if OK, otherwise PI_BAD_SCRIPT_ID.
+   The function returns 0 if OK, otherwise PI_BAD_SCRIPT_ID, or
+   PI_TOO_MANY_PARAM.
+
+   param is an array of up to 10 parameters which may be referenced in
+   the script as param 0 to param 9.
+*/
+
+
+
+/* ----------------------------------------------------------------------- */
+int gpioScriptStatus(int script_id, uint32_t *param);
+/* ----------------------------------------------------------------------- */
+/* This function returns the run status of a stored script as well as
+   the current values of parameters 0 to 9.
+
+   The function returns greater than or equal to 0 if OK,
+   otherwise PI_BAD_SCRIPT_ID.
+
+   The run status may be
+
+   PI_SCRIPT_HALTED
+   PI_SCRIPT_RUNNING
+   PI_SCRIPT_WAITING
+   PI_SCRIPT_FAILED
+
+   The current value of script parameters 0 to 9 are returned in param.
 */
 
+/* script status */
+
+#define PI_SCRIPT_HALTED  0
+#define PI_SCRIPT_RUNNING 1
+#define PI_SCRIPT_WAITING 2
+#define PI_SCRIPT_FAILED  3
+
 
 
 /* ----------------------------------------------------------------------- */
@@ -1798,12 +1863,98 @@ int gpioCfgInternals(unsigned what,
    Not intended for general use.
 */
 
+/*-------------------------------------------------------------------------*/
+int gpioWaveAddSPI(
+   gpioSPI_t *spi,
+   unsigned offset,
+   unsigned ss,
+   uint8_t *tx_bits,
+   unsigned num_tx_bits,
+   unsigned rx_bit_first,
+   unsigned rx_bit_last,
+   unsigned bits);
+/*-------------------------------------------------------------------------*/
+/* This function adds a waveform representing SPI data to the
+   existing waveform (if any).  The SPI data starts offset microseconds
+   from the start of the waveform.  ss is the slave select gpio.  bits bits
+   are transferred.  num_tx_bits are transmitted starting at the first bit.
+   The bits to transmit are read, most significant bit first, from tx_bits.
+   Gpio reads are made from rx_bit_first to rx_bit_last.
 
+   Returns the new total number of pulses in the current waveform if OK,
+   otherwise PI_BAD_USER_GPIO, PI_BAD_SER_OFFSET, or PI_TOO_MANY_PULSES.
+*/
+
+
+/* ----------------------------------------------------------------------- */
+uint32_t waveGetRawOut(int pos);
+/* ----------------------------------------------------------------------- */
+/* Gets the wave output parameter stored at pos.
+   Not intended for general use.
+*/
 
+
+/* ----------------------------------------------------------------------- */
+void waveSetRawOut(int pos, uint32_t value);
+/* ----------------------------------------------------------------------- */
+/* Sets the wave output parameter stored at pos to value.
+   Not intended for general use.
+*/
+
+/* ----------------------------------------------------------------------- */
+uint32_t waveGetRawIn(int pos);
+/* ----------------------------------------------------------------------- */
+/* Gets the wave input value parameter stored at pos.
+   Not intended for general use.
+*/
+
+
+/* ----------------------------------------------------------------------- */
+void waveSetRawIn(int pos, uint32_t value);
+/* ----------------------------------------------------------------------- */
+/* Sets the wave input value stored at pos to value.
+   Not intended for general use.
+*/
+
+/*-------------------------------------------------------------------------*/
+int getBitInBytes(int bitPos, uint8_t *buf, int numBits);
 /*-------------------------------------------------------------------------*/
-void gpioWaveDump(void);
+/* Returns the value of the bit bitPos bits from the start of buf.  Returns
+   0 if bitPos is greater than or equal to numBits.
+*/
+
+/* ----------------------------------------------------------------------- */
+void putBitInBytes(int bitPos, uint8_t *buf, int val);
 /*-------------------------------------------------------------------------*/
-/* Used to print a readable version of the current waveform to stdout.
+/* Sets the bit bitPos bits from the start of buf to val.
+*/
+
+/*-------------------------------------------------------------------------*/
+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
+*/
+
+
+/*-------------------------------------------------------------------------*/
+void gpioDumpWave(void);
+/*-------------------------------------------------------------------------*/
+/* Used to print a readable version of the current waveform to stderr.
+   Not intended for general use.
+*/
+
+
+/*-------------------------------------------------------------------------*/
+void gpioDumpScript(int s);
+/*-------------------------------------------------------------------------*/
+/* Used to print a readable version of a script to stderr.
    Not intended for general use.
 */
 
@@ -1859,6 +2010,10 @@ void gpioWaveDump(void);
 #define PI_CMD_SLRO  42
 #define PI_CMD_SLR   43
 #define PI_CMD_SLRC  44
+#define PI_CMD_PROCP 45
+#define PI_CMD_MICRO 46
+#define PI_CMD_MILLI 47
+
 
 /*
 The following command only works on the socket interface.
@@ -1872,6 +2027,53 @@ after this command is issued.
 
 #define PI_CMD_NOIB  99
 
+/* pseudo commands */
+
+#define PI_CMD_SCRIPT 800
+
+#define PI_CMD_ADDI  800
+#define PI_CMD_ADDV  801
+#define PI_CMD_ANDI  802
+#define PI_CMD_ANDV  803
+#define PI_CMD_CALL  804
+#define PI_CMD_CMPI  805
+#define PI_CMD_CMPV  806
+#define PI_CMD_DCRA  807
+#define PI_CMD_DCRV  808
+#define PI_CMD_HALT  809
+#define PI_CMD_INRA  810
+#define PI_CMD_INRV  811
+#define PI_CMD_JM    812
+#define PI_CMD_JMP   813
+#define PI_CMD_JNZ   814
+#define PI_CMD_JP    815
+#define PI_CMD_JZ    816
+#define PI_CMD_LABEL 817
+#define PI_CMD_LDAI  818
+#define PI_CMD_LDAP  819
+#define PI_CMD_LDAV  820
+#define PI_CMD_LDPA  821
+#define PI_CMD_LDVA  822
+#define PI_CMD_LDVI  823
+#define PI_CMD_LDVV  824
+#define PI_CMD_ORI   827
+#define PI_CMD_ORV   828
+#define PI_CMD_POPA  829
+#define PI_CMD_POPV  830
+#define PI_CMD_PUSHA 831
+#define PI_CMD_PUSHV 832
+#define PI_CMD_RET   833
+#define PI_CMD_RAL   834
+#define PI_CMD_RAR   835
+#define PI_CMD_SUBI  836
+#define PI_CMD_SUBV  837
+#define PI_CMD_SWAPA 838
+#define PI_CMD_SWAPV 839
+#define PI_CMD_SYS   840
+#define PI_CMD_WAITI 841
+#define PI_CMD_WAITV 842
+#define PI_CMD_XORI  843
+#define PI_CMD_XORV  844
 
 /*-------------------------------------------------------------------------*/
 
@@ -1930,7 +2132,17 @@ after this command is issued.
 #define PI_BAD_SER_OFFSET   -49 /* add serial data offset > 30 minutes     */
 #define PI_GPIO_IN_USE      -50 /* gpio already in use                     */
 #define PI_BAD_SERIAL_COUNT -51 /* must read at least a byte at a time     */
-
+#define PI_BAD_PARAM_NUM    -52 /* script parameter must be 0-9            */
+#define PI_DUP_LABEL        -53 /* script has duplicate label              */
+#define PI_TOO_MANY_LABELS  -54 /* script has too many labels              */
+#define PI_BAD_SCRIPT_CMD   -55 /* illegal script command                  */
+#define PI_BAD_VAR_NUM      -56 /* script variable must be 0-149           */
+#define PI_NO_SCRIPT_ROOM   -57 /* no more room for scripts                */
+#define PI_NO_MEMORY        -58 /* can't allocate temporary memory         */
+#define PI_SOCK_READ_FAILED -59 /* socket read failed                      */
+#define PI_SOCK_WRIT_FAILED -60 /* socket write failed                     */
+#define PI_TOO_MANY_PARAM   -61 /* too many script parameters > 10         */
+#define PI_NOT_HALTED       -62 /* script already running or failed        */
 
 /*-------------------------------------------------------------------------*/
 
index dd74105978ce558448902d9eaffba19d219ffcf5..f5591ca0fd2b50ee6004927df67efced23cd364d 100644 (file)
--- a/pigpio.py
+++ b/pigpio.py
@@ -76,7 +76,7 @@ import threading
 import os
 import atexit
 
-VERSION = "1.3"
+VERSION = "1.4"
 
 # gpio levels
 
@@ -113,6 +113,13 @@ PUD_OFF  = 0
 PUD_DOWN = 1
 PUD_UP   = 2
 
+# script run status
+
+PI_SCRIPT_HALTED =0
+PI_SCRIPT_RUNNING=1
+PI_SCRIPT_WAITING=2
+PI_SCRIPT_FAILED =3
+
 # pigpio command numbers
 
 _PI_CMD_MODES= 0
@@ -160,6 +167,7 @@ _PI_CMD_PROCS=41
 _PI_CMD_SLRO= 42
 _PI_CMD_SLR=  43
 _PI_CMD_SLRC= 44
+_PI_CMD_PROCP=45
 
 
 _PI_CMD_NOIB= 99
@@ -205,7 +213,7 @@ _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_NOT_SERIAL_GPIO  =-38
 _PI_BAD_SERIAL_STRUC=-39
 _PI_BAD_SERIAL_BUF  =-40
 PI_NOT_PERMITTED    =-41
@@ -218,6 +226,18 @@ PI_BAD_SCRIPT       =-47
 PI_BAD_SCRIPT_ID    =-48
 PI_BAD_SER_OFFSET   =-49
 PI_GPIO_IN_USE      =-50
+PI_BAD_SERIAL_COUNT =-51
+PI_BAD_PARAM_NUM    =-52
+PI_DUP_LABEL        =-53
+PI_TOO_MANY_LABELS  =-54
+PI_BAD_SCRIPT_CMD   =-55
+PI_BAD_VAR_NUM      =-56
+PI_NO_SCRIPT_ROOM   =-57
+PI_NO_MEMORY        =-58
+PI_SOCK_READ_FAILED =-59
+PI_SOCK_WRIT_FAILED =-60
+PI_TOO_MANY_PARAM   =-61
+PI_NOT_HALTED       =-62
 
 # pigpio error text
 
@@ -270,6 +290,19 @@ _errors=[
    [PI_BAD_SCRIPT_ID     , "unknown script id"],
    [PI_BAD_SER_OFFSET    , "add serial data offset > 30 minute"],
    [PI_GPIO_IN_USE       , "gpio already in use"],
+   [PI_BAD_SERIAL_COUNT  , "must read at least a byte at a time"],
+   [PI_BAD_PARAM_NUM     , "script parameter must be 0-9"],
+   [PI_DUP_LABEL         , "script has duplicate label"],
+   [PI_TOO_MANY_LABELS   , "script has too many labels"],
+   [PI_BAD_SCRIPT_CMD    , "illegal script command"],
+   [PI_BAD_VAR_NUM       , "script variable must be 0-149"],
+   [PI_NO_SCRIPT_ROOM    , "no more room for scripts"],
+   [PI_NO_MEMORY         , "can't allocate temporary memory"],
+   [PI_SOCK_READ_FAILED  , "socket read failed"],
+   [PI_SOCK_WRIT_FAILED  , "socket write failed"],
+   [PI_TOO_MANY_PARAM    , "too many script parameters (> 10)"],
+   [PI_NOT_HALTED        , "script already running or failed"],
+
 ]
 
 _control = None
@@ -348,8 +381,8 @@ def _pigpio_command(sock, cmd, p1, p2):
 
    sock: command socket.
    cmd:  the command to be executed.
-   p1:   command paramter 1 (if applicable).
-   p2:   command paramter 2 (if applicable).
+   p1:   command parameter 1 (if applicable).
+   p2:   command parameter 2 (if applicable).
    """
    if sock is not None:
       sock.send(struct.pack('IIII', cmd, p1, p2, 0))
@@ -364,8 +397,8 @@ def _pigpio_command_ext(sock, cmd, p1, p2, extents):
 
    sock: command socket.
    cmd:  the command to be executed.
-   p1:   command paramter 1 (if applicable).
-   p2:   command paramter 2 (if applicable).
+   p1:   command parameter 1 (if applicable).
+   p2:   command parameter 2 (if applicable).
    extents: additional data blocks
    """
    if sock is not None:
@@ -1676,15 +1709,57 @@ def store_script(script):
    return _u2i(_pigpio_command_ext(
       _control, _PI_CMD_PROC, len(script), 0, script))
 
-def run_script(script_id):
+def run_script(script_id, params=None):
    """
    Runs a stored script.
 
    Returns 0 if OK, otherwise PI_BAD_SCRIPT_ID.
 
    script_id: script_id of stored script.
+   params: up to 10 parameters required by the script.
+   """
+   # I p1 script id
+   # I p2 number of parameters (0-10)
+   ## (optional) extension ##
+   # I[] params
+
+   if params is not None:
+      msg = ""
+      for p in params:
+         msg += struct.pack("I", p)
+      nump = len(params)
+      extents = [msg]
+   else:
+      nump = 0
+      extents = []
+
+   return _u2i(_pigpio_command_ext(
+      _control, _PI_CMD_PROCR, script_id, nump, extents))
+
+def script_status(script_id):
+   """
+   This function returns the run status of a stored script as well as
+   the current values of parameters 0 to 9.
+
+   The function returns greater than or equal to 0 if OK,
+   otherwise PI_BAD_SCRIPT_ID.
+
+   The run status may be
+
+   PI_SCRIPT_HALTED
+   PI_SCRIPT_RUNNING
+   PI_SCRIPT_WAITING
+   PI_SCRIPT_FAILED
+
+   It returns a tuple of run status and a parameter list tuple. If the script
+   does not exist a negative error code will be returned in which
+   case the parameter tuple will be empty.
    """
-   return _u2i(_pigpio_command(_control, _PI_CMD_PROCR, script_id, 0))
+   status = _u2i(_pigpio_command(_control, _PI_CMD_PROCP, script_id, 0))
+   if status >= 0:
+      param = struct.unpack('IIIIIIIIII', _control.recv(40))
+      return status, param
+   return status, ()
 
 def stop_script(script_id):
    """
@@ -2015,7 +2090,8 @@ def start(host = os.getenv("PIGPIO_ADDR", ''),
       print("Did you specify the correct Pi host/port in the")
       print("pigpio.start() function? E.g. pigpio.start('soft', 8888))")
       print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%")
-      raise
+      return False
+   return True
 
 def stop():
    """Release pigpio resources.
index 4960150eeeb873fe6fb6b8a48773682e7879fbec..2fab6d0b272fa5419c3e3285412e764a1d9ec1b1 100644 (file)
@@ -25,7 +25,7 @@ OTHER DEALINGS IN THE SOFTWARE.
 For more information, please refer to <http://unlicense.org/>
 */
 
-/* PIGPIOD_IF_VERSION 3 */
+/* PIGPIOD_IF_VERSION 4 */
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -534,10 +534,10 @@ int get_mode(unsigned gpio)
 int set_pull_up_down(unsigned gpio, unsigned pud)
    {return pigpio_command(gPigCommand, PI_CMD_PUD, gpio, pud);}
 
-int read_gpio(unsigned gpio)
+int gpio_read(unsigned gpio)
    {return pigpio_command(gPigCommand, PI_CMD_READ, gpio, 0);}
 
-int write_gpio(unsigned gpio, unsigned level)
+int gpio_write(unsigned gpio, unsigned level)
    {return pigpio_command(gPigCommand, PI_CMD_WRITE, gpio, level);}
 
 int set_PWM_dutycycle(unsigned user_gpio, unsigned dutycycle)
@@ -600,7 +600,7 @@ uint32_t get_current_tick(void)
 uint32_t get_hardware_revision(void)
    {return pigpio_command(gPigCommand, PI_CMD_HWVER, 0, 0);}
 
-unsigned get_pigpio_version(void)
+uint32_t get_pigpio_version(void)
    {return pigpio_command(gPigCommand, PI_CMD_PIGPV, 0, 0);}
 
 int wave_clear(void)
@@ -725,8 +725,41 @@ int store_script(char *script)
    return pigpio_command_ext(gPigCommand, PI_CMD_PROC, len, 0, 1, ext);
 }
 
-int run_script(unsigned script_id)
-   {return pigpio_command(gPigCommand, PI_CMD_PROCR, script_id, 0);}
+int run_script(unsigned script_id, unsigned numPar, uint32_t *param)
+{
+   gpioExtent_t ext[1];
+
+   /*
+   p1=script id
+   p2=number of parameters
+   ## extension ##
+   uint32_t[] parameters
+   */
+
+   ext[0].size = sizeof(uint32_t)*numPar;
+   ext[0].ptr = param;
+
+   return pigpio_command_ext
+      (gPigCommand, PI_CMD_PROCR, script_id, numPar, 1, ext);
+}
+
+int script_status(int script_id, uint32_t *param)
+{
+   int status;
+   uint32_t p[MAX_SCRIPT_PARAMS];
+
+   status = pigpio_command(gPigCommand, PI_CMD_PROCP, script_id, 0);
+
+   if (status >= 0)
+   {
+      /* get the data */
+      recv(gPigCommand, p, sizeof(p), MSG_WAITALL);
+
+      if (param) memcpy(param, p, sizeof(p));
+   }
+
+   return status;
+}
 
 int stop_script(unsigned script_id)
    {return pigpio_command(gPigCommand, PI_CMD_PROCS, script_id, 0);}
index c8e8fc5221d8429507cbcc05b60c16ae6de4d5a9..809800c4d10a48ceb9991201afb5393e72b7cd5d 100644 (file)
@@ -30,7 +30,7 @@ For more information, please refer to <http://unlicense.org/>
 
 #include "pigpio.h"
 
-#define PIGPIOD_IF_VERSION 3
+#define PIGPIOD_IF_VERSION 4
 
 typedef enum
 {
@@ -79,7 +79,7 @@ pthread_t *start_thread(gpioThreadFunc_t func, void *arg);
    The function is passed the single argument arg.
 
    The thread can be cancelled by passing the pointer to pthread_t to
-   gpioStopThread().
+   stop_thread().
 */
 
 void stop_thread(pthread_t *pth);
@@ -87,7 +87,7 @@ void stop_thread(pthread_t *pth);
 
    No value is returned.
 
-   The thread to be stopped should have been started with gpioStartThread().
+   The thread to be stopped should have been started with start_thread().
 */
 
 int pigpio_start(char *addrStr, char *portStr);
@@ -139,7 +139,7 @@ int set_pull_up_down(unsigned gpio, unsigned pud);
    pud:  PUD_UP, PUD_DOWN, PUD_OFF.
 */
 
-int read_gpio(unsigned gpio);
+int gpio_read(unsigned gpio);
 /* Read the gpio level.
 
    Returns the gpio level if OK, otherwise PI_BAD_GPIO.
@@ -147,7 +147,7 @@ int read_gpio(unsigned gpio);
    gpio:0-53.
 */
 
-int write_gpio(unsigned gpio, unsigned level);
+int gpio_write(unsigned gpio, unsigned level);
 /*
    Write the gpio level.
 
@@ -507,6 +507,12 @@ uint32_t get_hardware_revision(void);
    hexadecimal number the function returns 0.
 */
 
+uint32_t get_pigpio_version(void);
+/*
+   Returns the pigpio version.
+*/
+
+
 int wave_clear(void);
 /* This function initialises a new waveform.
 
@@ -659,10 +665,31 @@ int store_script(char *script);
    otherwise PI_BAD_SCRIPT.
 */
 
-int run_script(unsigned script_id);
+int run_script(unsigned script_id, unsigned numPar, uint32_t *param);
 /* This function runs a stored script.
 
-   The function returns 0 if OK, otherwise PI_BAD_SCRIPT_ID.
+   The function returns 0 if OK, otherwise PI_BAD_SCRIPT_ID, or
+   PI_TOO_MANY_PARAM
+
+   param is an array of up to 10 parameters which may be referenced in
+   the script as param 0 to param 9..
+*/
+
+int script_status(int script_id, uint32_t *param);
+/* This function returns the run status of a stored script as well
+   as the current values of parameters 0 to 9.
+
+   The function returns greater than or equal to 0 if OK,
+   otherwise PI_BAD_SCRIPT_ID.
+
+   The run status may be
+
+   PI_SCRIPT_HALTED
+   PI_SCRIPT_RUNNING
+   PI_SCRIPT_WAITING
+   PI_SCRIPT_FAILED
+
+   The current value of script parameters 0 to 9 are returned in param.
 */
 
 int stop_script(unsigned script_id);
@@ -684,7 +711,7 @@ int serial_read_open(unsigned user_gpio, unsigned baud);
    or PI_GPIO_IN_USE.
 
    The serial data is returned in a cyclic buffer and is read using
-   gpioSerialRead().
+   serial_read().
 
    It is the caller's responsibility to read data from the cyclic buffer
    in a timely fashion.
diff --git a/pigs.c b/pigs.c
index 4043c708a7d728f5196466cdb7c7b0087aad68c4..35c47e4cc2c2223fe94f4a05b450e86e78eefa88 100644 (file)
--- a/pigs.c
+++ b/pigs.c
@@ -26,7 +26,7 @@ For more information, please refer to <http://unlicense.org/>
 */
 
 /*
-This version is for pigpio version 12+
+This version is for pigpio version 13+
 */
 
 #include <stdio.h>
@@ -47,6 +47,9 @@ This program provides a socket interface to some of
 the commands available from pigpio.
 */
 
+char command_buf[8192];
+char response_buf[8192];
+
 void fatal(char *fmt, ...)
 {
    char buf[128];
@@ -103,109 +106,226 @@ static int openSocket(void)
    return sock;
 }
 
+void print_result(int sock, int rv, cmdCmd_t cmd)
+{
+   int i, r;
+   uint32_t *p;
+
+   r = cmd.res;
+
+   switch (rv)
+   {
+      case 0:
+         if (r < 0) fatal("ERROR: %s", cmdErrStr(r));
+         break;
+
+      case 1:
+         if (r < 0) fatal("ERROR: %s", cmdErrStr(r));
+         break;
+
+      case 2:
+         if (r < 0) fatal("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;
+
+      case 6: /* SLR */
+         if (r < 0) fatal("ERROR: %s", cmdErrStr(r));
+         else if (r > 0)
+         {
+            printf("%s", response_buf);
+         }
+         break;
+
+      case 7: /* PROCP */
+         if (r < 0) fatal("ERROR: %s", cmdErrStr(r));
+         else
+         {
+            printf("%d", r);
+
+            p = (uint32_t *)response_buf;
+
+            for (i=0; i<MAX_SCRIPT_PARAMS; i++)
+            {
+               printf(" %d", p[i]);
+            }
+
+            printf("\n");
+         }
+         break;
+   }
+}
+
+void get_extensions(int sock, int command, int res)
+{
+   switch (command)
+   {
+      case PI_CMD_PROCP: /* PROCP */
+         if (res >= 0)
+         {
+            recv(sock,
+                 response_buf,
+                 sizeof(uint32_t)*MAX_SCRIPT_PARAMS,
+                 MSG_WAITALL);
+         }
+         break;
+
+      case PI_CMD_SLR: /* SLR */
+         if (res > 0)
+         {
+            recv(sock, response_buf, res, MSG_WAITALL);
+            response_buf[res] = 0;
+         }
+         break;
+   }
+}
+
+void put_extensions(int sock, int command, uint32_t *p, void *v[])
+{
+   switch (command)
+   {
+      case PI_CMD_PROC:
+         /*
+         p1=script length w[1]
+         p2=0
+         ## extension ##
+         char[] script    v[1]
+         */
+         send(sock, v[1], p[1], 0);
+         break;
+
+      case PI_CMD_PROCR:
+         /*
+         p1=script id     w[1]
+         p2=numParam      w[2]
+         ## extension ##
+         int[] param      v[1]
+         */
+         if (p[2]) send(sock, v[1], p[2]*sizeof(uint32_t), 0);
+         break;
+
+      case PI_CMD_TRIG:
+         /*
+         p1=user_gpio      w[1]
+         p2=pulseLen       w[2]
+         ## extension ##
+         unsigned level    w[3]
+         */
+         send(sock, &p[3], 4, 0);
+         break;
+
+      case PI_CMD_WVAG:
+         /*
+         p1=pulses        w[1]
+         p2=0
+         ## extension ##
+         int[] param      v[1]
+         */
+         if (p[1]) send(sock, v[1], p[1]*sizeof(gpioPulse_t), 0);
+         break;
+
+      case PI_CMD_WVAS:
+         /*
+         p1=user_gpio       w[1]
+         p2=numChar         w[4]
+         ## extension ##
+         unsigned baud      w[2]
+         unsigned offset    w[3]
+         char[] str         v[1]
+         */
+         send(sock, &p[2], 4, 0);
+         send(sock, &p[3], 4, 0);
+         send(sock, v[1], p[4], 0);
+         break;
+   }
+
+}
+
 int main(int argc , char *argv[])
 {
-   int sock, r, idx, i;
+   int sock, command;
+   int idx, i, pp, l, len;
    cmdCmd_t cmd;
-   gpioExtent_t ext[3];
-   char buf[1024];
+   uint32_t p[10];
+   void *v[10];
+   gpioCtlParse_t ctl;
 
    sock = openSocket(); 
 
    if (sock != -1)
    {
-      switch(argc)
-      {
-         case 1:
-            exit(0);
-
-         case 2:
-            sprintf(buf, "%10s", argv[1]);
-            break;
+      command_buf[0] = 0;
+      l = 0;
+      pp = 0;
 
-         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;
+      for (i=1; i<argc; i++)
+      {
+         l += (strlen(argv[i]) + 1);
+         if (l < sizeof(command_buf))
+            {sprintf(command_buf+pp, "%s ", argv[i]); pp=l;}
+      }
 
-         case 5:
-            sprintf(buf, "%10s %10s %10s %10s",
-               argv[1], argv[2], argv[3], argv[4]);
-            break;
+      if (pp) {command_buf[--pp] = 0;}
 
-         case 6:
-            sprintf(buf, "%10s %10s %10s %10s %10s",
-               argv[1], argv[2], argv[3], argv[4], argv[5]);
-            break;
+      ctl.flags = 0;
+      ctl.eaten = 0;
 
-         default:
-            fatal("what? 'pigs h' for help");
-      }
+      len = strlen(command_buf);
+      idx = 0;
 
-      if ((idx=cmdParse(buf, &cmd, argc, argv, ext)) >= 0)
+      while ((idx >= 0) && (ctl.eaten < len))
       {
-         if (send(sock, &cmd, sizeof(cmdCmd_t), 0) == sizeof(cmdCmd_t))
+         if ((idx=cmdParse(command_buf, p, v, &ctl)) >= 0)
          {
-            /* send extensions */
+            command = p[0];
 
-            for (i=0; i<cmdInfo[idx].ext; i++)
+            if (command < PI_CMD_SCRIPT)
             {
-               send(sock, ext[i].ptr, ext[i].size, 0);
-            }
+               cmd.cmd = command;
+               cmd.p1 = p[1];
+               cmd.p2 = p[2];
 
-            if (recv(sock, &cmd, sizeof(cmdCmd_t), MSG_WAITALL) ==
-               sizeof(cmdCmd_t))
-            {
-               switch (cmdInfo[idx].rv)
+               switch (command)
                {
-                  case 0:
-                     r = cmd.res;
-                     if (r < 0) fatal("ERROR: %s", cmdErrStr(r));
-                     break;
+                  case PI_CMD_WVAS:
+                     cmd.p2 = p[4];
+                    break;
 
-                  case 1:
-                     r = cmd.res;
-                     if (r < 0) fatal("ERROR: %s", cmdErrStr(r));
-                     break;
-
-                  case 2:
-                     r = cmd.res;
-                     if (r < 0) fatal("ERROR: %s", cmdErrStr(r));
-                     else printf("%d\n", r);
-                     break;
-
-                  case 3:
-                     printf("%08X\n", cmd.res);
+                  case PI_CMD_PROC:
+                     cmd.p2 = 0;
                      break;
+               }
 
-                  case 4:
-                     printf("%u\n", cmd.res);
-                     break;
+               if (send(sock, &cmd, sizeof(cmdCmd_t), 0) == sizeof(cmdCmd_t))
+               {
+                  put_extensions(sock, command, p, v);
 
-                  case 5:
-                     printf(cmdUsage);
-                     break;
+                  if (recv(sock, &cmd, sizeof(cmdCmd_t), MSG_WAITALL) ==
+                     sizeof(cmdCmd_t))
+                  {
+                     get_extensions(sock, command, cmd.res);
 
-                  case 6:
-                     r = cmd.res;
-                     if (r < 0) fatal("ERROR: %s", cmdErrStr(r));
-                     else if (r > 0)
-                     {
-                        recv(sock, &buf, r, MSG_WAITALL);
-                        buf[r] = 0;
-                        printf("%s", buf);
-                     }
-                     break;
+                     print_result(sock, cmdInfo[idx].rv, cmd);
+                  }
+                  else fatal("recv failed, %m");
                }
+               else fatal("send failed, %m");
             }
-            else fatal("recv failed, %m");
+            else fatal("%s only allowed within a script", cmdInfo[idx].name);
          }
-         else fatal("send failed, %m");
+         else fatal("%s? pigs h for help", cmdStr());
       }
-      else fatal("what? 'pigs h' for help");
    }
    else fatal("connect failed, %m");
 
index 3a8ea36645eee211fef2dd878714c0e388a27ee3..dc6e3040c0015c2a87593e14bf7490b61146b855 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -3,15 +3,15 @@
 from distutils.core import setup
 
 setup(name='pigpio',
-      version='1.3',
+      version='1.4',
       author='joan',
-      author_email='joan@abyz.me.uk',
+      author_email='joan@abyz.co.uk',
       maintainer='joan',
-      maintainer_email='joan@abyz.me.uk',
+      maintainer_email='joan@abyz.co.uk',
       url='http://abyz.co.uk/rpi/pigpio/python.html/',
       description='Raspberry Pi gpio module',
-      long_description='Raspberry Pi Python module for access to the pigpio daemon',
+      long_description='Raspberry Pi Python module to access the pigpio daemon',
       download_url='http://abyz.co.uk/rpi/pigpio/pigpio.zip',
-      license='TBD',
+      license='unlicense.org',
       py_modules=['pigpio']
      )
diff --git a/x_pigpio.c b/x_pigpio.c
new file mode 100644 (file)
index 0000000..d884f4d
--- /dev/null
@@ -0,0 +1,638 @@
+/*
+gcc -o x_pigpio x_pigpio.c -lpigpio -lrt -lpthread
+sudo ./x_pigpio
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "pigpio.h"
+
+#define USERDATA 18249013
+
+#define GPIO 4
+
+void CHECK(int t, int st, int got, int expect, int pc, char *desc)
+{
+   if ((got >= (((1E2-pc)*expect)/1E2)) && (got <= (((1E2+pc)*expect)/1E2)))
+   {
+      printf("TEST %2d.%-2d PASS (%s: %d)\n", t, st, desc, expect);
+   }
+   else
+   {
+      fprintf(stderr,
+              "TEST %2d.%-2d FAILED got %d (%s: %d)\n",
+              t, st, got, desc, expect);
+   }
+}
+
+void t0()
+{
+   printf("Version.\n");
+
+   printf("pigpio version %d.\n", gpioVersion());
+
+   printf("Hardware revision %d.\n", gpioHardwareRevision());
+}
+
+void t1()
+{
+   int v;
+
+   printf("Mode/PUD/read/write tests.\n");
+
+   gpioSetMode(GPIO, PI_INPUT);
+   v = gpioGetMode(GPIO);
+   CHECK(1, 1, v, 0, 0, "set mode, get mode");
+
+   gpioSetPullUpDown(GPIO, PI_PUD_UP);
+   v = gpioRead(GPIO);
+   CHECK(1, 2, v, 1, 0, "set pull up down, read");
+
+   gpioSetPullUpDown(GPIO, PI_PUD_DOWN);
+   v = gpioRead(GPIO);
+   CHECK(1, 3, v, 0, 0, "set pull up down, read");
+
+   gpioWrite(GPIO, PI_LOW);
+   v = gpioGetMode(GPIO);
+   CHECK(1, 4, v, 1, 0, "write, get mode");
+
+   v = gpioRead(GPIO);
+   CHECK(1, 5, v, 0, 0, "read");
+
+   gpioWrite(GPIO, PI_HIGH);
+   v = gpioRead(GPIO);
+   CHECK(1, 6, v, 1, 0, "write, read");
+}
+
+int t2_count=0;
+
+void t2cb(int gpio, int level, uint32_t tick)
+{
+   t2_count++;
+}
+
+void t2()
+{
+   int f, r, rr, oc;
+
+   printf("PWM dutycycle/range/frequency tests.\n");
+
+   gpioSetPWMrange(GPIO, 255);
+   gpioSetPWMfrequency(GPIO, 0);
+   f = gpioGetPWMfrequency(GPIO);
+   CHECK(2, 1, f, 10, 0, "set PWM range, set/get PWM frequency");
+
+   gpioSetAlertFunc(GPIO, t2cb);
+
+   gpioPWM(GPIO, 0);
+   time_sleep(0.5); /* allow old notifications to flush */
+   oc = t2_count;
+   time_sleep(2);
+   f = t2_count - oc;
+   CHECK(2, 2, f, 0, 0, "set PWM dutycycle, callback");
+
+   gpioPWM(GPIO, 128);
+   oc = t2_count;
+   time_sleep(2);
+   f = t2_count - oc;
+   CHECK(2, 3, f, 40, 5, "set PWM dutycycle, callback");
+
+   gpioSetPWMfrequency(GPIO, 100);
+   f = gpioGetPWMfrequency(GPIO);
+   CHECK(2, 4, f, 100, 0, "set/get PWM frequency");
+
+   oc = t2_count;
+   time_sleep(2);
+   f = t2_count - oc;
+   CHECK(2, 5, f, 400, 1, "callback");
+
+   gpioSetPWMfrequency(GPIO, 1000);
+   f = gpioGetPWMfrequency(GPIO);
+   CHECK(2, 6, f, 1000, 0, "set/get PWM frequency");
+
+   oc = t2_count;
+   time_sleep(2);
+   f = t2_count - oc;
+   CHECK(2, 7, f, 4000, 1, "callback");
+
+   r = gpioGetPWMrange(GPIO);
+   CHECK(2, 8, r, 255, 0, "get PWM range");
+
+   rr = gpioGetPWMrealRange(GPIO);
+   CHECK(2, 9, rr, 200, 0, "get PWM real range");
+
+   gpioSetPWMrange(GPIO, 2000);
+   r = gpioGetPWMrange(GPIO);
+   CHECK(2, 10, r, 2000, 0, "set/get PWM range");
+
+   rr = gpioGetPWMrealRange(GPIO);
+   CHECK(2, 11, rr, 200, 0, "get PWM real range");
+
+   gpioPWM(GPIO, 0);
+}
+
+int t3_val = USERDATA;
+int t3_reset=1;
+int t3_count=0;
+uint32_t t3_tick=0;
+float t3_on=0.0;
+float t3_off=0.0;
+
+void t3cbf(int gpio, int level, uint32_t tick, void *userdata)
+{
+   static int unreported = 1;
+
+   uint32_t td;
+   int *val;
+
+   val = userdata;
+
+   if (*val != USERDATA)
+   {
+      if (unreported)
+      {
+         fprintf
+         (
+            stderr,
+            "unexpected userdata %d (expected %d)\n",
+            *val, USERDATA
+         );
+      }
+      unreported = 0;
+   }
+
+   if (t3_reset)
+   {
+      t3_count = 0;
+      t3_on = 0.0;
+      t3_off = 0.0;
+      t3_reset = 0;
+   }
+   else
+   {
+      td = tick - t3_tick;
+
+      if (level == 0) t3_on += td;
+      else            t3_off += td;
+   }
+
+   t3_count ++;
+   t3_tick = tick;
+}
+
+void t3()
+{
+   int f, rr;
+
+   float on, off;
+
+   int t;
+
+   int pw[3]={500, 1500, 2500};
+   int dc[4]={20, 40, 60, 80};
+
+   printf("PWM/Servo pulse accuracy tests.\n");
+
+   gpioSetAlertFuncEx(GPIO, t3cbf, &t3_val); /* test extended alert */
+
+   for (t=0; t<3; t++)
+   {
+      gpioServo(GPIO, pw[t]);
+      time_sleep(1);
+      t3_reset = 1;
+      time_sleep(4);
+      on = t3_on;
+      off = t3_off;
+      CHECK(3, 1+t, (1E3*(on+off))/on, 2E7/pw[t], 1,
+          "set servo pulsewidth");
+   }
+
+   gpioServo(GPIO, 0);
+   gpioSetPWMfrequency(GPIO, 1000);
+   f = gpioGetPWMfrequency(GPIO);
+   CHECK(3, 4, f, 1000, 0, "set/get PWM frequency");
+
+   rr = gpioSetPWMrange(GPIO, 100);
+   CHECK(3, 5, rr, 200, 0, "set PWM range");
+
+   for (t=0; t<4; t++)
+   {
+      gpioPWM(GPIO, dc[t]);
+      time_sleep(1);
+      t3_reset = 1;
+      time_sleep(2);
+      on = t3_on;
+      off = t3_off;
+      CHECK(3, 6+t, (1E3*on)/(on+off), 1E1*dc[t], 1,
+         "set PWM dutycycle");
+   }
+
+   gpioPWM(GPIO, 0);
+}
+
+void t4()
+{
+   int h, e, f, n, s, b, l, seq_ok, toggle_ok;
+   gpioReport_t r;
+   char p[32];
+
+   printf("Pipe notification tests.\n");
+
+   gpioSetPWMfrequency(GPIO, 0);
+   gpioPWM(GPIO, 0);
+   gpioSetPWMrange(GPIO, 100);
+
+   h = gpioNotifyOpen();
+   e = gpioNotifyBegin(h, (1<<4));
+   CHECK(4, 1, e, 0, 0, "notify open/begin");
+
+   time_sleep(1);
+
+   sprintf(p, "/dev/pigpio%d", h);
+
+   f = open(p, O_RDONLY);
+
+   gpioPWM(GPIO, 50);
+   time_sleep(4);
+   gpioPWM(GPIO, 0);
+
+   e = gpioNotifyPause(h);
+   CHECK(4, 2, e, 0, 0, "notify pause");
+
+   e = gpioNotifyClose(h);
+   CHECK(4, 3, e, 0, 0, "notify close");
+
+   n = 0;
+   s = 0;
+   l = 0;
+   seq_ok = 1;
+   toggle_ok = 1;
+
+   while (1)
+   {
+      b = read(f, &r, 12);
+      if (b == 12)
+      {
+         if (s != r.seqno) seq_ok = 0;
+
+         if (n) if (l != (r.level&(1<<4))) toggle_ok = 0;
+
+         if (r.level&(1<<4)) l = 0;
+         else                l = (1<<4);
+           
+         s++;
+         n++;
+
+         // printf("%d %d %d %X\n", r.seqno, r.flags, r.tick, r.level);
+      }
+      else break;
+   }
+
+   close(f);
+
+   CHECK(4, 4, seq_ok, 1, 0, "sequence numbers ok");
+
+   CHECK(4, 5, toggle_ok, 1, 0, "gpio toggled ok");
+
+   CHECK(4, 6, n, 80, 10, "number of notifications");
+}
+
+int t5_count = 0;
+
+void t5cbf(int gpio, int level, uint32_t tick)
+{
+   if (level == 0) t5_count++; /* falling edges */
+}
+
+void t5()
+{
+   int BAUD=4800;
+
+   char *TEXT=
+"\n\
+Now is the winter of our discontent\n\
+Made glorious summer by this sun of York;\n\
+And all the clouds that lour'd upon our house\n\
+In the deep bosom of the ocean buried.\n\
+Now are our brows bound with victorious wreaths;\n\
+Our bruised arms hung up for monuments;\n\
+Our stern alarums changed to merry meetings,\n\
+Our dreadful marches to delightful measures.\n\
+Grim-visaged war hath smooth'd his wrinkled front;\n\
+And now, instead of mounting barded steeds\n\
+To fright the souls of fearful adversaries,\n\
+He capers nimbly in a lady's chamber\n\
+To the lascivious pleasing of a lute.\n\
+";
+
+   gpioPulse_t wf[] =
+   {
+      {1<<GPIO, 0,  10000},
+      {0, 1<<GPIO,  30000},
+      {1<<GPIO, 0,  60000},
+      {0, 1<<GPIO, 100000},
+   };
+
+   int e, oc, c;
+
+   char text[2048];
+
+   printf("Waveforms & serial read/write tests.\n");
+
+   gpioSetAlertFunc(GPIO, t5cbf);
+
+   gpioSetMode(GPIO, PI_OUTPUT);
+
+   e = gpioWaveClear();
+   CHECK(5, 1, e, 0, 0, "callback, set mode, wave clear");
+
+   e = gpioWaveAddGeneric(4, wf);
+   CHECK(5, 2, e, 4, 0, "pulse, wave add generic");
+
+   e = gpioWaveTxStart(PI_WAVE_MODE_REPEAT);
+   CHECK(5, 3, e, 9, 0, "wave tx repeat");
+
+   oc = t5_count;
+   time_sleep(5);
+   c = t5_count - oc;
+   CHECK(5, 4, c, 50, 1, "callback");
+
+   e = gpioWaveTxStop();
+   CHECK(5, 5, e, 0, 0, "wave tx stop");
+
+   /* gpioSerialReadOpen changes the alert function */
+
+   e = gpioSerialReadOpen(GPIO, BAUD);
+   CHECK(5, 6, e, 0, 0, "serial read open");
+
+   gpioWaveClear();
+   e = gpioWaveAddSerial(GPIO, BAUD, 5000000, strlen(TEXT), TEXT);
+   CHECK(5, 7, e, 3405, 0, "wave clear, wave add serial");
+
+   e = gpioWaveTxStart(PI_WAVE_MODE_ONE_SHOT);
+   CHECK(5, 8, e, 6811, 0, "wave tx start");
+
+   CHECK(5, 9, 0, 0, 0, "NOT APPLICABLE");
+
+   CHECK(5, 10, 0, 0, 0, "NOT APPLICABLE");
+
+   while (gpioWaveTxBusy()) time_sleep(0.1);
+   time_sleep(0.1);
+   c = gpioSerialRead(GPIO, text, sizeof(text)-1);
+   if (c > 0) text[c] = 0;
+   CHECK(5, 11, strcmp(TEXT, text), 0, 0, "wave tx busy, serial read");
+
+   e = gpioSerialReadClose(GPIO);
+   CHECK(5, 12, e, 0, 0, "serial read close");
+
+   c = gpioWaveGetMicros();
+   CHECK(5, 13, c, 6158704, 0, "wave get micros");
+
+   c = gpioWaveGetHighMicros();
+   CHECK(5, 14, c, 6158704, 0, "wave get high micros");
+
+   c = gpioWaveGetMaxMicros();
+   CHECK(5, 15, c, 1800000000, 0, "wave get max micros");
+
+   c = gpioWaveGetPulses();
+   CHECK(5, 16, c, 3405, 0, "wave get pulses");
+
+   c = gpioWaveGetHighPulses();
+   CHECK(5, 17, c, 3405, 0, "wave get high pulses");
+
+   c = gpioWaveGetMaxPulses();
+   CHECK(5, 18, c, 12000, 0, "wave get max pulses");
+
+   c = gpioWaveGetCbs();
+   CHECK(5, 19, c, 6810, 0, "wave get cbs");
+
+   c = gpioWaveGetHighCbs();
+   CHECK(5, 20, c, 6810, 0, "wave get high cbs");
+
+   c = gpioWaveGetMaxCbs();
+   CHECK(5, 21, c, 25016, 0, "wave get max cbs");
+}
+
+int t6_count=0;
+int t6_on=0;
+uint32_t t6_on_tick=0;
+
+void t6cbf(int gpio, int level, uint32_t tick)
+{
+   if (level == 1)
+   {
+      t6_on_tick = tick;
+      t6_count++;
+   }
+   else
+   {
+      if (t6_on_tick) t6_on += (tick - t6_on_tick);
+   }
+}
+
+void t6()
+{
+   int tp, t, p;
+
+   printf("Trigger tests\n");
+
+   gpioWrite(GPIO, PI_LOW);
+
+   tp = 0;
+
+   gpioSetAlertFunc(GPIO, t6cbf);
+
+   for (t=0; t<10; t++)
+   {
+      time_sleep(0.1);
+      p = 10 + (t*10);
+      tp += p;
+      gpioTrigger(4, p, 1);
+   }
+
+   time_sleep(0.2);
+
+   CHECK(6, 1, t6_count, 10, 0, "gpio trigger count");
+
+   CHECK(6, 2, t6_on, tp, 25, "gpio trigger pulse length");
+}
+
+int t7_count=0;
+
+void t7cbf(int gpio, int level, uint32_t tick)
+{
+   if (level == PI_TIMEOUT) t7_count++;
+}
+
+void t7()
+{
+   int c, oc;
+
+   printf("Watchdog tests.\n");
+
+   /* type of edge shouldn't matter for watchdogs */
+   gpioSetAlertFunc(GPIO, t7cbf);
+
+   gpioSetWatchdog(GPIO, 10); /* 10 ms, 100 per second */
+   time_sleep(0.5);
+   oc = t7_count;
+   time_sleep(2);
+   c = t7_count - oc;
+   CHECK(7, 1, c, 200, 1, "set watchdog on count");
+
+   gpioSetWatchdog(GPIO, 0); /* 0 switches watchdog off */
+   time_sleep(0.5);
+   oc = t7_count;
+   time_sleep(2);
+   c = t7_count - oc;
+   CHECK(7, 2, c, 0, 1, "set watchdog off count");
+}
+
+void t8()
+{
+   int v, t, i;
+
+   printf("Bank read/write tests.\n");
+
+   gpioWrite(GPIO, 0);
+   v = gpioRead_Bits_0_31() & (1<<GPIO);
+   CHECK(8, 1, v, 0, 0, "read bank 1");
+
+   gpioWrite(GPIO, 1);
+   v = gpioRead_Bits_0_31() & (1<<GPIO);
+   CHECK(8, 2, v, (1<<GPIO), 0, "read bank 1");
+
+   gpioWrite_Bits_0_31_Clear(1<<GPIO);
+   v = gpioRead(GPIO);
+   CHECK(8, 3, v, 0, 0, "clear bank 1");
+
+   gpioWrite_Bits_0_31_Set(1<<GPIO);
+   v = gpioRead(GPIO);
+   CHECK(8, 4, v, 1, 0, "set bank 1");
+
+   t = 0;
+   v = (1<<16);
+   for (i=0; i<100; i++)
+   {
+      if (gpioRead_Bits_32_53() & v) t++;
+   };
+   CHECK(8, 5, t, 60, 75, "read bank 2");
+
+   v = gpioWrite_Bits_32_53_Clear(0);
+   CHECK(8, 6, v, 0, 0, "clear bank 2");
+
+   CHECK(8, 7, 0, 0, 0, "NOT APPLICABLE");
+
+   v = gpioWrite_Bits_32_53_Set(0);
+   CHECK(8, 8, v, 0, 0, "set bank 2");
+
+   CHECK(8, 9, 0, 0, 0, "NOT APPLICABLE");
+}
+
+int t9_count = 0;
+
+void t9cbf(int gpio, int level, uint32_t tick)
+{
+   if (level == 1) t9_count++;
+}
+
+void t9()
+{
+   int s, oc, c, e;
+   uint32_t p[10];
+
+   /*
+   100 loops per second
+   p0 number of loops
+   p1 GPIO
+   */
+   char *script="\
+   ldap 0\
+   ldva 0\
+   label 0\
+   w p1 1\
+   milli 5\
+   w p1 0\
+   milli 5\
+   dcrv 0\
+   ldav 0\
+   ldpa 9\
+   jp 0";
+
+   printf("Script store/run/status/stop/delete tests.\n");
+
+   gpioWrite(GPIO, 0); /* need known state */
+
+   gpioSetAlertFunc(GPIO, t9cbf);
+
+   s = gpioStoreScript(script);
+   time_sleep(0.1);
+   oc = t9_count;
+   p[0] = 99;
+   p[1] = GPIO;
+   gpioRunScript(s, 2, p);
+   time_sleep(2);
+   c = t9_count - oc;
+   CHECK(9, 1, c, 100, 0, "store/run script");
+
+   oc = t9_count;
+   p[0] = 200;
+   p[1] = GPIO;
+   gpioRunScript(s, 2, p);
+   time_sleep(0.1);
+   while (1)
+   {
+      e = gpioScriptStatus(s, p);
+      if (e != PI_SCRIPT_RUNNING) break;
+      time_sleep(0.5);
+   }
+   c = t9_count - oc;
+   time_sleep(0.1);
+   CHECK(9, 2, c, 201, 0, "run script/script status");
+
+   oc = t9_count;
+   p[0] = 2000;
+   p[1] = GPIO;
+   gpioRunScript(s, 2, p);
+   time_sleep(0.1);
+   while (1)
+   {
+      e = gpioScriptStatus(s, p);
+      if (e != PI_SCRIPT_RUNNING) break;
+      if (p[9] < 1900) gpioStopScript(s);
+      time_sleep(0.1);
+   }
+   c = t9_count - oc;
+   time_sleep(0.1);
+   CHECK(9, 3, c, 110, 10, "run/stop script/script status");
+
+   e = gpioDeleteScript(s);
+   CHECK(9, 4, e, 0, 0, "delete script");
+}
+
+int main(int argc, char *argv[])
+{
+   int t, status;
+   void (*test[])(void) = {t0, t1, t2, t3, t4, t5, t6, t7, t8, t9};
+
+   status = gpioInitialise();
+
+   if (status < 0)
+   {
+      fprintf(stderr, "pigpio initialisation failed.\n");
+      return 1;
+   }
+
+   for (t=0; t<10; t++) test[t]();
+
+   gpioTerminate();
+
+   return 0;
+}
+
diff --git a/x_pigpio.py b/x_pigpio.py
new file mode 100755 (executable)
index 0000000..7b09908
--- /dev/null
@@ -0,0 +1,540 @@
+#!/usr/bin/env python
+
+import time
+import struct
+
+import pigpio
+
+GPIO=4
+
+def CHECK(t, st, got, expect, pc, desc):
+   if got >= (((1E2-pc)*expect)/1E2) and got <= (((1E2+pc)*expect)/1E2):
+      print("TEST {:2d}.{:<2d} PASS ({}: {:d})".format(t, st, desc, expect))
+   else:
+      print("TEST {:2d}.{:<2d} FAILED got {:d} ({}: {:d})".
+         format(t, st, got, desc, expect))
+
+def t0():
+   print("Version.")
+
+   print("pigpio version {}.".format(pigpio.get_pigpio_version()))
+
+   print("Hardware revision {}.".format(pigpio.get_hardware_revision()))
+
+def t1():
+
+   print("Mode/PUD/read/write tests.")
+
+   pigpio.set_mode(GPIO, pigpio.INPUT)
+   v = pigpio.get_mode(GPIO)
+   CHECK(1, 1, v, 0, 0, "set mode, get mode")
+
+   pigpio.set_pull_up_down(GPIO, pigpio.PUD_UP)
+   v = pigpio.read(GPIO)
+   CHECK(1, 2, v, 1, 0, "set pull up down, read")
+
+   pigpio.set_pull_up_down(GPIO, pigpio.PUD_DOWN)
+   v = pigpio.read(GPIO)
+   CHECK(1, 3, v, 0, 0, "set pull up down, read")
+
+   pigpio.write(GPIO, pigpio.LOW)
+   v = pigpio.get_mode(GPIO)
+   CHECK(1, 4, v, 1, 0, "write, get mode")
+
+   v = pigpio.read(GPIO)
+   CHECK(1, 5, v, 0, 0, "read")
+
+   pigpio.write(GPIO, pigpio.HIGH)
+   v = pigpio.read(GPIO)
+   CHECK(1, 6, v, 1, 0, "write, read")
+
+t2_count=0
+
+def t2cbf(gpio, level, tick):
+   global t2_count
+   t2_count += 1
+
+def t2():
+   global t2_count
+
+   print("PWM dutycycle/range/frequency tests.")
+
+   pigpio.set_PWM_range(GPIO, 255)
+   pigpio.set_PWM_frequency(GPIO,0)
+   f = pigpio.get_PWM_frequency(GPIO)
+   CHECK(2, 1, f, 10, 0, "set PWM range, set/get PWM frequency")
+
+   t2cb = pigpio.callback(GPIO, pigpio.EITHER_EDGE, t2cbf)
+
+   pigpio.set_PWM_dutycycle(GPIO, 0)
+   time.sleep(0.5) # allow old notifications to flush
+   oc = t2_count
+   time.sleep(2)
+   f = t2_count - oc
+   CHECK(2, 2, f, 0, 0, "set PWM dutycycle, callback")
+
+   pigpio.set_PWM_dutycycle(GPIO, 128)
+   time.sleep(1)
+   oc = t2_count
+   time.sleep(2)
+   f = t2_count - oc
+   CHECK(2, 3, f, 40, 5, "set PWM dutycycle, callback")
+
+   pigpio.set_PWM_frequency(GPIO,100)
+   f = pigpio.get_PWM_frequency(GPIO)
+   CHECK(2, 4, f, 100, 0, "set/get PWM frequency")
+
+   time.sleep(1)
+   oc = t2_count
+   time.sleep(2)
+   f = t2_count - oc
+   CHECK(2, 5, f, 400, 1, "callback")
+
+   pigpio.set_PWM_frequency(GPIO,1000)
+   f = pigpio.get_PWM_frequency(GPIO)
+   CHECK(2, 6, f, 1000, 0, "set/get PWM frequency")
+
+   time.sleep(1)
+   oc = t2_count
+   time.sleep(2)
+   f = t2_count - oc
+   CHECK(2, 7, f, 4000, 1, "callback")
+
+   r = pigpio.get_PWM_range(GPIO)
+   CHECK(2, 8, r, 255, 0, "get PWM range")
+
+   rr = pigpio.get_PWM_real_range(GPIO)
+   CHECK(2, 9, rr, 200, 0, "get PWM real range")
+
+   pigpio.set_PWM_range(GPIO, 2000)
+   r = pigpio.get_PWM_range(GPIO)
+   CHECK(2, 10, r, 2000, 0, "set/get PWM range")
+
+   rr = pigpio.get_PWM_real_range(GPIO)
+   CHECK(2, 11, rr, 200, 0, "get PWM real range")
+
+   pigpio.set_PWM_dutycycle(GPIO, 0)
+
+t3_reset=True
+t3_count=0
+t3_tick=0
+t3_on=0.0
+t3_off=0.0
+
+def t3cbf(gpio, level, tick):
+   global t3_reset, t3_count, t3_tick, t3_on, t3_off
+
+   if t3_reset:
+      t3_count = 0
+      t3_on = 0.0
+      t3_off = 0.0
+      t3_reset = False
+   else:
+      td = pigpio.tickDiff(t3_tick, tick)
+
+      if level == 0:
+         t3_on += td
+      else:
+         t3_off += td
+
+   t3_count += 1
+   t3_tick = tick
+
+def t3():
+   global t3_reset, t3_count, t3_on, t3_off
+
+   pw=[500.0, 1500.0, 2500.0]
+   dc=[0.2, 0.4, 0.6, 0.8]
+
+   print("PWM/Servo pulse accuracy tests.")
+
+   t3cb = pigpio.callback(GPIO, pigpio.EITHER_EDGE, t3cbf)
+
+   t = 0
+   for x in pw:
+      t += 1
+      pigpio.set_servo_pulsewidth(GPIO, x)
+      time.sleep(1)
+      t3_reset = True
+      time.sleep(4)
+      c = t3_count
+      on = t3_on
+      off = t3_off
+      CHECK(3, t, int((1E3*(on+off))/on), int(2E7/x), 1, "set servo pulsewidth")
+
+
+   pigpio.set_servo_pulsewidth(GPIO, 0)
+   pigpio.set_PWM_frequency(GPIO, 1000)
+   f = pigpio.get_PWM_frequency(GPIO)
+   CHECK(3, 4, f, 1000, 0, "set/get PWM frequency")
+
+   rr = pigpio.set_PWM_range(GPIO, 100)
+   CHECK(3, 5, rr, 200, 0, "set PWM range")
+
+   t = 5
+   for x in dc:
+      t += 1
+      pigpio.set_PWM_dutycycle(GPIO, x*100)
+      time.sleep(1)
+      t3_reset = True
+      time.sleep(2)
+      c = t3_count
+      on = t3_on
+      off = t3_off
+      CHECK(3, t, int((1E3*on)/(on+off)), int(1E3*x), 1, "set PWM dutycycle")
+
+   pigpio.set_PWM_dutycycle(GPIO, 0)
+
+def t4():
+
+   print("Pipe notification tests.")
+
+   pigpio.set_PWM_frequency(GPIO, 0)
+   pigpio.set_PWM_dutycycle(GPIO, 0)
+   pigpio.set_PWM_range(GPIO, 100)
+
+   h = pigpio.notify_open()
+   e = pigpio.notify_begin(h, (1<<4))
+   CHECK(4, 1, e, 0, 0, "notify open/begin")
+
+   time.sleep(1)
+
+   with open("/dev/pigpio"+ str(h), "rb") as f:
+
+      pigpio.set_PWM_dutycycle(GPIO, 50)
+      time.sleep(4)
+      pigpio.set_PWM_dutycycle(GPIO, 0)
+
+      e = pigpio.notify_pause(h)
+      CHECK(4, 2, e, 0, 0, "notify pause")
+
+      e = pigpio.notify_close(h)
+      CHECK(4, 3, e, 0, 0, "notify close")
+
+      n = 0
+      s = 0
+
+      seq_ok = 1
+      toggle_ok = 1
+
+      while True:
+
+         chunk = f.read(12)
+
+         if len(chunk) == 12:
+
+            S, fl, t, v = struct.unpack('HHII', chunk)
+            if s != S:
+               seq_ok = 0
+
+
+            L = v & (1<<4)
+
+            if n:
+               if l != L:
+                  toggle_ok = 0
+
+            if L:
+               l = 0
+            else:
+               l = (1<<4)
+           
+            s += 1
+            n += 1
+
+            # print(S, fl, t, hex(v))
+
+         else:
+            break
+
+      f.close()
+
+      CHECK(4, 4, seq_ok, 1, 0, "sequence numbers ok")
+
+      CHECK(4, 5, toggle_ok, 1, 0, "gpio toggled ok")
+
+      CHECK(4, 6, n, 80, 10, "number of notifications")
+
+t5_count = 0
+
+def t5cbf(gpio, level, tick):
+   global t5_count
+   t5_count += 1
+
+def t5():
+   global t5_count
+
+   BAUD=4800
+
+   TEXT="""
+Now is the winter of our discontent
+Made glorious summer by this sun of York;
+And all the clouds that lour'd upon our house
+In the deep bosom of the ocean buried.
+Now are our brows bound with victorious wreaths;
+Our bruised arms hung up for monuments;
+Our stern alarums changed to merry meetings,
+Our dreadful marches to delightful measures.
+Grim-visaged war hath smooth'd his wrinkled front;
+And now, instead of mounting barded steeds
+To fright the souls of fearful adversaries,
+He capers nimbly in a lady's chamber
+To the lascivious pleasing of a lute.
+"""
+
+   print("Waveforms & serial read/write tests.")
+
+   t5cb = pigpio.callback(GPIO, pigpio.FALLING_EDGE, t5cbf)
+
+   pigpio.set_mode(GPIO, pigpio.OUTPUT)
+
+   e = pigpio.wave_clear()
+   CHECK(5, 1, e, 0, 0, "callback, set mode, wave clear")
+
+   wf = []
+
+   wf.append(pigpio.pulse(1<<GPIO, 0,  10000))
+   wf.append(pigpio.pulse(0, 1<<GPIO,  30000))
+   wf.append(pigpio.pulse(1<<GPIO, 0,  60000))
+   wf.append(pigpio.pulse(0, 1<<GPIO, 100000))
+
+   e = pigpio.wave_add_generic(wf)
+   CHECK(5, 2, e, 4, 0, "pulse, wave add generic")
+
+   e = pigpio.wave_tx_repeat()
+   CHECK(5, 3, e, 9, 0, "wave tx repeat")
+
+   oc = t5_count
+   time.sleep(5)
+   c = t5_count - oc
+   CHECK(5, 4, c, 50, 1, "callback")
+
+   e = pigpio.wave_tx_stop()
+   CHECK(5, 5, e, 0, 0, "wave tx stop")
+
+   e = pigpio.serial_read_open(GPIO, BAUD)
+   CHECK(5, 6, e, 0, 0, "serial read open")
+
+   pigpio.wave_clear()
+   e = pigpio.wave_add_serial(GPIO, BAUD, 5000000, TEXT)
+   CHECK(5, 7, e, 3405, 0, "wave clear, wave add serial")
+
+   e = pigpio.wave_tx_start()
+   CHECK(5, 8, e, 6811, 0, "wave tx start")
+
+   oc = t5_count
+   time.sleep(3)
+   c = t5_count - oc
+   CHECK(5, 9, c, 0, 0, "callback")
+
+   oc = t5_count
+   while pigpio.wave_tx_busy():
+      time.sleep(0.1)
+   time.sleep(0.1)
+   c = t5_count - oc
+   CHECK(5, 10, c, 1702, 0, "wave tx busy, callback")
+
+   c, text = pigpio.serial_read(GPIO)
+   CHECK(5, 11, TEXT == text, True, 0, "wave tx busy, serial read");
+
+   e = pigpio.serial_read_close(GPIO)
+   CHECK(5, 12, e, 0, 0, "serial read close")
+
+   c = pigpio.wave_get_micros()
+   CHECK(5, 13, c, 6158704, 0, "wave get micros")
+
+   CHECK(5, 14, 0, 0, 0, "NOT APPLICABLE")
+
+   c = pigpio.wave_get_max_micros()
+   CHECK(5, 15, c, 1800000000, 0, "wave get max micros")
+
+   c = pigpio.wave_get_pulses()
+   CHECK(5, 16, c, 3405, 0, "wave get pulses")
+
+   CHECK(5, 17, 0, 0, 0, "NOT APPLICABLE")
+
+   c = pigpio.wave_get_max_pulses()
+   CHECK(5, 18, c, 12000, 0, "wave get max pulses")
+
+   c = pigpio.wave_get_cbs()
+   CHECK(5, 19, c, 6810, 0, "wave get cbs")
+
+   CHECK(5, 20, 0, 0, 0, "NOT APPLICABLE")
+
+   c = pigpio.wave_get_max_cbs()
+   CHECK(5, 21, c, 25016, 0, "wave get max cbs")
+
+t6_count=0
+t6_on=0
+t6_on_tick=None
+
+def t6cbf(gpio, level, tick):
+   global t6_count, t6_on, t6_on_tick
+   if level == 1:
+      t6_on_tick = tick
+      t6_count += 1
+   else:
+      if t6_on_tick is not None:
+         t6_on += pigpio.tickDiff(t6_on_tick, tick)
+
+def t6():
+   global t6_count, t6_on
+
+   print("Trigger tests.")
+
+   pigpio.write(GPIO, pigpio.LOW)
+
+   tp = 0
+
+   t6cb = pigpio.callback(GPIO, pigpio.EITHER_EDGE, t6cbf)
+
+   for t in range(10):
+      time.sleep(0.1)
+      p = 10 + (t*10)
+      tp += p;
+      pigpio.gpio_trigger(4, p, 1)
+
+   time.sleep(0.5)
+
+   CHECK(6, 1, t6_count, 10, 0, "gpio trigger count")
+
+   CHECK(6, 2, t6_on, tp, 25, "gpio trigger pulse length")
+
+t7_count=0
+
+def t7cbf(gpio, level, tick):
+   global t7_count
+   if level == pigpio.TIMEOUT:
+      t7_count += 1
+
+def t7():
+   global t7_count
+
+   print("Watchdog tests.")
+
+   # type of edge shouldn't matter for watchdogs
+   t7cb = pigpio.callback(GPIO, pigpio.FALLING_EDGE, t7cbf)
+
+   pigpio.set_watchdog(GPIO, 10) # 10 ms, 100 per second
+   time.sleep(0.5)
+   oc = t7_count
+   time.sleep(2)
+   c = t7_count - oc
+   CHECK(7, 1, c, 200, 1, "set watchdog on count")
+
+   pigpio.set_watchdog(GPIO, 0) # 0 switches watchdog off
+   time.sleep(0.5)
+   oc = t7_count
+   time.sleep(2)
+   c = t7_count - oc
+   CHECK(7, 2, c, 0, 1, "set watchdog off count")
+
+def t8():
+   print("Bank read/write tests.")
+
+   pigpio.write(GPIO, 0)
+   v = pigpio.read_bank_1() & (1<<GPIO)
+   CHECK(8, 1, v, 0, 0, "read bank 1")
+
+   pigpio.write(GPIO, 1)
+   v = pigpio.read_bank_1() & (1<<GPIO)
+   CHECK(8, 2, v, (1<<GPIO), 0, "read bank 1")
+
+   pigpio.clear_bank_1(1<<GPIO)
+   v = pigpio.read(GPIO)
+   CHECK(8, 3, v, 0, 0, "clear bank 1")
+
+   pigpio.set_bank_1(1<<GPIO)
+   v = pigpio.read(GPIO)
+   CHECK(8, 4, v, 1, 0, "set bank 1")
+
+   t = 0
+   v = (1<<16)
+   for i in range(100):
+      if pigpio.read_bank_2() & v:
+         t += 1
+   CHECK(8, 5, t, 60, 75, "read bank 2")
+
+   v = pigpio.clear_bank_2(0)
+   CHECK(8, 6, v, 0, 0, "clear bank 2")
+
+   pigpio.exceptions = False
+   v = pigpio.clear_bank_2(0xffffff)
+   pigpio.exceptions = True
+   CHECK(8, 7, v, pigpio.PI_SOME_PERMITTED, 0, "clear bank 2")
+
+   v = pigpio.set_bank_2(0)
+   CHECK(8, 8, v, 0, 0, "set bank 2")
+
+   pigpio.exceptions = False
+   v = pigpio.set_bank_2(0xffffff)
+   pigpio.exceptions = True
+   CHECK(8, 9, v, pigpio.PI_SOME_PERMITTED, 0, "set bank 2")
+
+def t9():
+   print("Script store/run/status/stop/delete tests.")
+
+   pigpio.write(GPIO, 0) # need known state
+
+   # 100 loops per second
+   # p0 number of loops
+   # p1 GPIO
+   script="""
+   ldap 0
+   ldva 0
+   label 0
+   w p1 1
+   milli 5
+   w p1 0
+   milli 5
+   dcrv 0
+   ldav 0
+   ldpa 9
+   jp 0"""
+
+   t9cb = pigpio.callback(GPIO)
+
+   s = pigpio.store_script(script)
+   oc = t9cb.tally()
+   pigpio.run_script(s, [99, GPIO])
+   time.sleep(2)
+   c = t9cb.tally() - oc
+   CHECK(9, 1, c, 100, 0, "store/run script")
+
+   oc = t9cb.tally()
+   pigpio.run_script(s, [200, GPIO])
+   while True:
+      e, p = pigpio.script_status(s)
+      if e != pigpio.PI_SCRIPT_RUNNING:
+         break
+      time.sleep(0.5)
+   c = t9cb.tally() - oc
+   time.sleep(0.1)
+   CHECK(9, 2, c, 201, 0, "run script/script status")
+
+   oc = t9cb.tally()
+   pigpio.run_script(s, [2000, GPIO])
+   while True:
+      e, p = pigpio.script_status(s)
+      if e != pigpio.PI_SCRIPT_RUNNING:
+         break
+      if p[9] < 1900:
+         pigpio.stop_script(s)
+      time.sleep(0.1)
+   c = t9cb.tally() - oc
+   time.sleep(0.1)
+   CHECK(9, 3, c, 110, 10, "run/stop script/script status")
+
+   e = pigpio.delete_script(s)
+   CHECK(9, 4, e, 0, 0, "delete script")
+
+if pigpio.start(''): # must run notification test on localhost
+   print("Connected to pigpio daemon.")
+
+   test = [t0, t1, t2, t3, t4, t5, t6, t7, t8, t9]
+
+   for t in test:
+      t();
+
+   pigpio.stop()
+
diff --git a/x_pigpiod_if.c b/x_pigpiod_if.c
new file mode 100644 (file)
index 0000000..9ae3229
--- /dev/null
@@ -0,0 +1,620 @@
+/*
+gcc -o x_pigpiod_if x_pigpiod_if.c -lpigpiod_if -lrt -lpthread
+sudo ./x_pigpiod_if
+*/
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "pigpiod_if.h"
+
+#define GPIO 4
+
+void CHECK(int t, int st, int got, int expect, int pc, char *desc)
+{
+   if ((got >= (((1E2-pc)*expect)/1E2)) && (got <= (((1E2+pc)*expect)/1E2)))
+   {
+      printf("TEST %2d.%-2d PASS (%s: %d)\n", t, st, desc, expect);
+   }
+   else
+   {
+      fprintf(stderr,
+              "TEST %2d.%-2d FAILED got %d (%s: %d)\n",
+              t, st, got, desc, expect);
+   }
+}
+
+void t0()
+{
+   printf("Version.\n");
+
+   printf("pigpio version %d.\n", get_pigpio_version());
+
+   printf("Hardware revision %d.\n", get_hardware_revision());
+}
+
+void t1()
+{
+   int v;
+
+   printf("Mode/PUD/read/write tests.\n");
+
+   set_mode(GPIO, PI_INPUT);
+   v = get_mode(GPIO);
+   CHECK(1, 1, v, 0, 0, "set mode, get mode");
+
+   set_pull_up_down(GPIO, PI_PUD_UP);
+   v = gpio_read(GPIO);
+   CHECK(1, 2, v, 1, 0, "set pull up down, read");
+
+   set_pull_up_down(GPIO, PI_PUD_DOWN);
+   v = gpio_read(GPIO);
+   CHECK(1, 3, v, 0, 0, "set pull up down, read");
+
+   gpio_write(GPIO, PI_LOW);
+   v = get_mode(GPIO);
+   CHECK(1, 4, v, 1, 0, "write, get mode");
+
+   v = gpio_read(GPIO);
+   CHECK(1, 5, v, 0, 0, "read");
+
+   gpio_write(GPIO, PI_HIGH);
+   v = gpio_read(GPIO);
+   CHECK(1, 6, v, 1, 0, "write, read");
+}
+
+int t2_count=0;
+
+void t2cb(unsigned gpio, unsigned level, uint32_t tick)
+{
+   t2_count++;
+}
+
+void t2()
+{
+   int f, r, rr, oc;
+
+   printf("PWM dutycycle/range/frequency tests.\n");
+
+   set_PWM_range(GPIO, 255);
+   set_PWM_frequency(GPIO, 0);
+   f = get_PWM_frequency(GPIO);
+   CHECK(2, 1, f, 10, 0, "set PWM range, set/get PWM frequency");
+
+   callback(GPIO, EITHER_EDGE, t2cb);
+
+   set_PWM_dutycycle(GPIO, 0);
+   time_sleep(0.5); /* allow old notifications to flush */
+   oc = t2_count;
+   time_sleep(2);
+   f = t2_count - oc;
+   CHECK(2, 2, f, 0, 0, "set PWM dutycycle, callback");
+
+   set_PWM_dutycycle(GPIO, 128);
+   oc = t2_count;
+   time_sleep(2);
+   f = t2_count - oc;
+   CHECK(2, 3, f, 40, 5, "set PWM dutycycle, callback");
+
+   set_PWM_frequency(GPIO, 100);
+   f = get_PWM_frequency(GPIO);
+   CHECK(2, 4, f, 100, 0, "set/get PWM frequency");
+
+   oc = t2_count;
+   time_sleep(2);
+   f = t2_count - oc;
+   CHECK(2, 5, f, 400, 1, "callback");
+
+   set_PWM_frequency(GPIO, 1000);
+   f = get_PWM_frequency(GPIO);
+   CHECK(2, 6, f, 1000, 0, "set/get PWM frequency");
+
+   oc = t2_count;
+   time_sleep(2);
+   f = t2_count - oc;
+   CHECK(2, 7, f, 4000, 1, "callback");
+
+   r = get_PWM_range(GPIO);
+   CHECK(2, 8, r, 255, 0, "get PWM range");
+
+   rr = get_PWM_real_range(GPIO);
+   CHECK(2, 9, rr, 200, 0, "get PWM real range");
+
+   set_PWM_range(GPIO, 2000);
+   r = get_PWM_range(GPIO);
+   CHECK(2, 10, r, 2000, 0, "set/get PWM range");
+
+   rr = get_PWM_real_range(GPIO);
+   CHECK(2, 11, rr, 200, 0, "get PWM real range");
+
+   set_PWM_dutycycle(GPIO, 0);
+}
+
+int t3_reset=1;
+int t3_count=0;
+uint32_t t3_tick=0;
+float t3_on=0.0;
+float t3_off=0.0;
+
+void t3cbf(unsigned gpio, unsigned level, uint32_t tick)
+{
+   uint32_t td;
+
+   if (t3_reset)
+   {
+      t3_count = 0;
+      t3_on = 0.0;
+      t3_off = 0.0;
+      t3_reset = 0;
+   }
+   else
+   {
+      td = tick - t3_tick;
+
+      if (level == 0) t3_on += td;
+      else            t3_off += td;
+   }
+
+   t3_count ++;
+   t3_tick = tick;
+}
+
+void t3()
+{
+   int pw[3]={500, 1500, 2500};
+   int dc[4]={20, 40, 60, 80};
+
+   int f, rr;
+   float on, off;
+
+   int t;
+
+   printf("PWM/Servo pulse accuracy tests.\n");
+
+   callback(GPIO, EITHER_EDGE, t3cbf);
+
+   for (t=0; t<3; t++)
+   {
+      set_servo_pulsewidth(GPIO, pw[t]);
+      time_sleep(1);
+      t3_reset = 1;
+      time_sleep(4);
+      on = t3_on;
+      off = t3_off;
+      CHECK(3, 1+t, (1000.0*(on+off))/on, 20000000.0/pw[t], 1,
+         "set servo pulsewidth");
+   }
+
+   set_servo_pulsewidth(GPIO, 0);
+   set_PWM_frequency(GPIO, 1000);
+   f = get_PWM_frequency(GPIO);
+   CHECK(3, 4, f, 1000, 0, "set/get PWM frequency");
+
+   rr = set_PWM_range(GPIO, 100);
+   CHECK(3, 5, rr, 200, 0, "set PWM range");
+
+   for (t=0; t<4; t++)
+   {
+      set_PWM_dutycycle(GPIO, dc[t]);
+      time_sleep(1);
+      t3_reset = 1;
+      time_sleep(2);
+      on = t3_on;
+      off = t3_off;
+      CHECK(3, 6+t, (1000.0*on)/(on+off), 10.0*dc[t], 1,
+         "set PWM dutycycle");
+   }
+
+   set_PWM_dutycycle(GPIO, 0);
+
+}
+
+void t4()
+{
+   int h, e, f, n, s, b, l, seq_ok, toggle_ok;
+   gpioReport_t r;
+   char p[32];
+
+   printf("Pipe notification tests.\n");
+
+   set_PWM_frequency(GPIO, 0);
+   set_PWM_dutycycle(GPIO, 0);
+   set_PWM_range(GPIO, 100);
+
+   h = notify_open();
+   e = notify_begin(h, (1<<4));
+   CHECK(4, 1, e, 0, 0, "notify open/begin");
+
+   time_sleep(1);
+
+   sprintf(p, "/dev/pigpio%d", h);
+
+   f = open(p, O_RDONLY);
+
+   set_PWM_dutycycle(GPIO, 50);
+   time_sleep(4);
+   set_PWM_dutycycle(GPIO, 0);
+
+   e = notify_pause(h);
+   CHECK(4, 2, e, 0, 0, "notify pause");
+
+   e = notify_close(h);
+   CHECK(4, 3, e, 0, 0, "notify close");
+
+   n = 0;
+   s = 0;
+   l = 0;
+   seq_ok = 1;
+   toggle_ok = 1;
+
+   while (1)
+   {
+      b = read(f, &r, 12);
+      if (b == 12)
+      {
+         if (s != r.seqno) seq_ok = 0;
+
+         if (n) if (l != (r.level&(1<<4))) toggle_ok = 0;
+
+         if (r.level&(1<<4)) l = 0;
+         else                l = (1<<4);
+           
+         s++;
+         n++;
+
+         // printf("%d %d %d %X\n", r.seqno, r.flags, r.tick, r.level);
+      }
+      else break;
+   }
+   close(f);
+   CHECK(4, 4, seq_ok, 1, 0, "sequence numbers ok");
+
+   CHECK(4, 5, toggle_ok, 1, 0, "gpio toggled ok");
+
+   CHECK(4, 6, n, 80, 10, "number of notifications");
+}
+
+int t5_count = 0;
+
+void t5cbf(unsigned gpio, unsigned level, uint32_t tick)
+{
+   t5_count++;
+}
+
+void t5()
+{
+   int BAUD=4800;
+
+   char *TEXT=
+"\n\
+Now is the winter of our discontent\n\
+Made glorious summer by this sun of York;\n\
+And all the clouds that lour'd upon our house\n\
+In the deep bosom of the ocean buried.\n\
+Now are our brows bound with victorious wreaths;\n\
+Our bruised arms hung up for monuments;\n\
+Our stern alarums changed to merry meetings,\n\
+Our dreadful marches to delightful measures.\n\
+Grim-visaged war hath smooth'd his wrinkled front;\n\
+And now, instead of mounting barded steeds\n\
+To fright the souls of fearful adversaries,\n\
+He capers nimbly in a lady's chamber\n\
+To the lascivious pleasing of a lute.\n\
+";
+
+   gpioPulse_t wf[] =
+   {
+      {1<<GPIO, 0,  10000},
+      {0, 1<<GPIO,  30000},
+      {1<<GPIO, 0,  60000},
+      {0, 1<<GPIO, 100000},
+   };
+
+   int e, oc, c;
+
+   char text[2048];
+
+   printf("Waveforms & serial read/write tests.\n");
+
+   callback(GPIO, FALLING_EDGE, t5cbf);
+
+   set_mode(GPIO, PI_OUTPUT);
+
+   e = wave_clear();
+   CHECK(5, 1, e, 0, 0, "callback, set mode, wave clear");
+
+   e = wave_add_generic(4, wf);
+   CHECK(5, 2, e, 4, 0, "pulse, wave add generic");
+
+   e = wave_tx_repeat();
+   CHECK(5, 3, e, 9, 0, "wave tx repeat");
+
+   oc = t5_count;
+   time_sleep(5);
+   c = t5_count - oc;
+   CHECK(5, 4, c, 50, 1, "callback");
+
+   e = wave_tx_stop();
+   CHECK(5, 5, e, 0, 0, "wave tx stop");
+
+   e = serial_read_open(GPIO, BAUD);
+   CHECK(5, 6, e, 0, 0, "serial read open");
+
+   wave_clear();
+   e = wave_add_serial(GPIO, BAUD, 5000000, strlen(TEXT), TEXT);
+   CHECK(5, 7, e, 3405, 0, "wave clear, wave add serial");
+
+   e = wave_tx_start();
+   CHECK(5, 8, e, 6811, 0, "wave tx start");
+
+   oc = t5_count;
+   time_sleep(3);
+   c = t5_count - oc;
+   CHECK(5, 9, c, 0, 0, "callback");
+
+   oc = t5_count;
+   while (wave_tx_busy()) time_sleep(0.1);
+   time_sleep(0.1);
+   c = t5_count - oc;
+   CHECK(5, 10, c, 1702, 0, "wave tx busy, callback");
+
+   c = serial_read(GPIO, text, sizeof(text)-1);
+   if (c > 0) text[c] = 0; /* null terminate string */
+   CHECK(5, 11, strcmp(TEXT, text), 0, 0, "wave tx busy, serial read");
+
+   e = serial_read_close(GPIO);
+   CHECK(5, 12, e, 0, 0, "serial read close");
+
+   c = wave_get_micros();
+   CHECK(5, 13, c, 6158704, 0, "wave get micros");
+
+   c = wave_get_high_micros();
+   CHECK(5, 14, c, 6158704, 0, "wave get high micros");
+
+   c = wave_get_max_micros();
+   CHECK(5, 15, c, 1800000000, 0, "wave get max micros");
+
+   c = wave_get_pulses();
+   CHECK(5, 16, c, 3405, 0, "wave get pulses");
+
+   c = wave_get_high_pulses();
+   CHECK(5, 17, c, 3405, 0, "wave get high pulses");
+
+   c = wave_get_max_pulses();
+   CHECK(5, 18, c, 12000, 0, "wave get max pulses");
+
+   c = wave_get_cbs();
+   CHECK(5, 19, c, 6810, 0, "wave get cbs");
+
+   c = wave_get_high_cbs();
+   CHECK(5, 20, c, 6810, 0, "wave get high cbs");
+
+   c = wave_get_max_cbs();
+   CHECK(5, 21, c, 25016, 0, "wave get max cbs");
+}
+
+int t6_count=0;
+int t6_on=0;
+uint32_t t6_on_tick=0;
+
+void t6cbf(unsigned gpio, unsigned level, uint32_t tick)
+{
+   if (level == 1)
+   {
+      t6_on_tick = tick;
+      t6_count++;
+   }
+   else
+   {
+      if (t6_on_tick) t6_on += (tick - t6_on_tick);
+   }
+}
+
+void t6()
+{
+   int tp, t, p;
+
+   printf("Trigger tests.\n");
+
+   gpio_write(GPIO, PI_LOW);
+
+   tp = 0;
+
+   callback(GPIO, EITHER_EDGE, t6cbf);
+
+   time_sleep(0.2);
+
+   for (t=0; t<10; t++)
+   {
+      time_sleep(0.1);
+      p = 10 + (t*10);
+      tp += p;
+      gpio_trigger(4, p, 1);
+   }
+
+   time_sleep(0.5);
+
+   CHECK(6, 1, t6_count, 10, 0, "gpio trigger count");
+
+   CHECK(6, 2, t6_on, tp, 25, "gpio trigger pulse length");
+}
+
+int t7_count=0;
+
+void t7cbf(unsigned gpio, unsigned level, uint32_t tick)
+{
+   if (level == PI_TIMEOUT) t7_count++;
+}
+
+void t7()
+{
+   int c, oc;
+
+   printf("Watchdog tests.\n");
+
+   /* type of edge shouldn't matter for watchdogs */
+   callback(GPIO, FALLING_EDGE, t7cbf);
+
+   set_watchdog(GPIO, 10); /* 10 ms, 100 per second */
+   time_sleep(0.5);
+   oc = t7_count;
+   time_sleep(2);
+   c = t7_count - oc;
+   CHECK(7, 1, c, 200, 1, "set watchdog on count");
+
+   set_watchdog(GPIO, 0); /* 0 switches watchdog off */
+   time_sleep(0.5);
+   oc = t7_count;
+   time_sleep(2);
+   c = t7_count - oc;
+   CHECK(7, 2, c, 0, 1, "set watchdog off count");
+}
+
+void t8()
+{
+   int v, t, i;
+
+   printf("Bank read/write tests.\n");
+
+   gpio_write(GPIO, 0);
+   v = read_bank_1() & (1<<GPIO);
+   CHECK(8, 1, v, 0, 0, "read bank 1");
+
+   gpio_write(GPIO, 1);
+   v = read_bank_1() & (1<<GPIO);
+   CHECK(8, 2, v, (1<<GPIO), 0, "read bank 1");
+
+   clear_bank_1(1<<GPIO);
+   v = gpio_read(GPIO);
+   CHECK(8, 3, v, 0, 0, "clear bank 1");
+
+   set_bank_1(1<<GPIO);
+   v = gpio_read(GPIO);
+   CHECK(8, 4, v, 1, 0, "set bank 1");
+
+   t = 0;
+   v = (1<<16);
+   for (i=0; i<100; i++)
+   {
+      if (read_bank_2() & v) t++;
+   };
+   CHECK(8, 5, t, 60, 75, "read bank 2");
+
+   v = clear_bank_2(0);
+   CHECK(8, 6, v, 0, 0, "clear bank 2");
+
+   v = clear_bank_2(0xffffff);
+   CHECK(8, 7, v, PI_SOME_PERMITTED, 0, "clear bank 2");
+
+   v = set_bank_2(0);
+   CHECK(8, 8, v, 0, 0, "set bank 2");
+
+   v = set_bank_2(0xffffff);
+   CHECK(8, 9, v, PI_SOME_PERMITTED, 0, "set bank 2");
+}
+
+int t9_count = 0;
+
+void t9cbf(unsigned gpio, unsigned level, uint32_t tick)
+{
+   t9_count++;
+}
+
+void t9()
+{
+   int s, oc, c, e;
+   uint32_t p[10];
+
+   printf("Script store/run/status/stop/delete tests.\n");
+
+   gpio_write(GPIO, 0); /* need known state */
+
+   /*
+   100 loops per second
+   p0 number of loops
+   p1 GPIO
+   */
+   char *script="\
+   ldap 0\
+   ldva 0\
+   label 0\
+   w p1 1\
+   milli 5\
+   w p1 0\
+   milli 5\
+   dcrv 0\
+   ldav 0\
+   ldpa 9\
+   jp 0";
+
+   callback(GPIO, RISING_EDGE, t9cbf);
+
+   s = store_script(script);
+   oc = t9_count;
+   p[0] = 99;
+   p[1] = GPIO;
+   run_script(s, 2, p);
+   time_sleep(2);
+   c = t9_count - oc;
+   CHECK(9, 1, c, 100, 0, "store/run script");
+
+   oc = t9_count;
+   p[0] = 200;
+   p[1] = GPIO;
+   run_script(s, 2, p);
+   while (1)
+   {
+      e = script_status(s, p);
+      if (e != PI_SCRIPT_RUNNING) break;
+      time_sleep(0.5);
+   }
+   c = t9_count - oc;
+   time_sleep(0.1);
+   CHECK(9, 2, c, 201, 0, "run script/script status");
+
+   oc = t9_count;
+   p[0] = 2000;
+   p[1] = GPIO;
+   run_script(s, 2, p);
+   while (1)
+   {
+      e = script_status(s, p);
+      if (e != PI_SCRIPT_RUNNING) break;
+      if (p[9] < 1900) stop_script(s);
+      time_sleep(0.1);
+   }
+   c = t9_count - oc;
+   time_sleep(0.1);
+   CHECK(9, 3, c, 110, 10, "run/stop script/script status");
+
+   e = delete_script(s);
+   CHECK(9, 4, e, 0, 0, "delete script");
+}
+
+int main(int argc, char *argv[])
+{
+   int t, status;
+   void (*test[])(void) = {t0, t1, t2, t3, t4, t5, t6, t7, t8, t9};
+
+   status = pigpio_start(0, 0);
+
+   if (status < 0)
+   {
+      fprintf(stderr, "pigpio initialisation failed.\n");
+      return 1;
+   }
+
+   printf("Connected to pigpio daemon.\n");
+
+   for (t=0; t<10; t++) test[t]();
+
+   pigpio_stop();
+
+   return 0;
+}
+
diff --git a/x_pigs b/x_pigs
new file mode 100755 (executable)
index 0000000..82d9124
--- /dev/null
+++ b/x_pigs
@@ -0,0 +1,271 @@
+#!/bin/bash
+
+GPIO=4
+
+#
+# This script serves as a confidence check that the socket interface to
+# the pigpio library is ok.
+#
+# The script uses gpio 4 (P1-7).  Make sure that nothing (or only a LED)
+# is connected to gpio 4 before running the script.
+#
+# To run the script
+# sudo pigpiod # if not already running on the Pi
+# export PIGPIO_ADDR=pi_host # to specify the Pi if testing remotely
+# ./x_pigs
+#
+
+s=$(pigs bc1 0)
+if [[ $s = "" ]]; then echo "BC1 ok"; else echo "BC1 fail ($s)"; fi
+
+s=$(pigs bc2 0)
+if [[ $s = "" ]]; then echo "BC2 ok"; else echo "BC2 fail ($s)"; fi
+
+s=$(pigs br1)
+if [[ ${#s} = 8 ]]
+then echo "BR1 ok"
+else echo "BR1 fail ($s)"
+fi
+
+s=$(pigs br2)
+if [[ ${#s} = 8 ]]
+then echo "BR2 ok"
+else echo "BR2 fail ($s)"
+fi
+
+s=$(pigs bs1 0)
+if [[ $s = "" ]]; then echo "BS1 ok"; else echo "BS1 fail ($s)"; fi
+
+s=$(pigs bs2 0)
+if [[ $s = "" ]]; then echo "BS2 ok"; else echo "BS2 fail ($s)"; fi
+
+s=$(pigs h)
+if [[ ${#s} = 2999 ]]; then echo "HELP ok"; else echo "HELP fail (${#s})"; fi
+
+s=$(pigs hwver)
+if [[ $s -ne 0 ]]; then echo "HWVER ok"; else echo "HWVER fail ($s)"; fi
+
+s=$(pigs micro 1000)
+if [[ $s = "" ]]; then echo "MICRO ok"; else echo "MICRO fail ($s)"; fi
+
+s=$(pigs milli 10)
+if [[ $s = "" ]]; then echo "MILLI ok"; else echo "MILLI fail ($s)"; fi
+
+s=$(pigs modes $GPIO 0)
+s=$(pigs modeg $GPIO)
+if [[ $s = 4 ]]; then echo "MODEG ok"; else echo "MODEG fail ($s)"; fi
+s=$(pigs m $GPIO r)
+s=$(pigs mg $GPIO)
+if [[ $s = 0 ]]; then echo "MODES ok"; else echo "MODES fail ($s)"; fi
+
+h=$(pigs no)
+if [[ $h -ge 0 && $h -le 31 ]]
+then echo "NO($h) ok"
+else echo "NO fail ($s)"
+fi
+
+s=$(pigs nb $h 0xabcd)
+if [[ $s = "" ]]; then echo "NB($h) ok"; else echo "NB fail ($s)"; fi
+
+s=$(pigs np $h)
+if [[ $s = "" ]]; then echo "NP($h) ok"; else echo "NP fail ($s)"; fi
+
+s=$(pigs nc $h)
+if [[ $s = "" ]]; then echo "NC($h) ok"; else echo "NC fail ($s)"; fi
+
+s=$(pigs pfs $GPIO 800)
+if [[ $s = 800 ]]; then echo "PFG-a ok"; else echo "PFG-a fail ($s)"; fi
+
+s=$(pigs pfg $GPIO)
+if [[ $s = 800 ]]; then echo "PFG-b ok"; else echo "PFG-b fail ($s)"; fi
+
+s=$(pigs pfs $GPIO 0)
+if [[ $s = 10 ]]; then echo "PFS-a ok"; else echo "PFS-a fail ($s)"; fi
+
+s=$(pigs pfs $GPIO 800)
+if [[ $s = 800 ]]; then echo "PFS-b ok"; else echo "PFS-b fail ($s)"; fi
+
+s=$(pigs pigpv)
+if [[ $s = 13 ]]; then echo "PIGPV ok"; else echo "PIGPV fail ($s)"; fi
+
+s=$(pigs prs $GPIO 255)
+if [[ $s = 250 ]]; then echo "PRG-a ok"; else echo "PRG-a fail ($s)"; fi
+s=$(pigs prg $GPIO)
+if [[ $s = 255 ]]; then echo "PRG-b ok"; else echo "PRG-b fail ($s)"; fi
+
+p=$(pigs proc ldap 0 ldpa 1 ldai 1234 ldpa 0 label 999 milli 1000 jmp 999)
+if [[ $p -ge 0 && $p -le 31 ]]
+then echo "PROC($p) ok"
+else echo "PROC($p) fail ($s)"
+fi
+
+s=$(pigs procr $p 9876)
+if [[ $s = 0 ]]; then echo "PROCR($p) ok"; else echo "PROCR($p) fail ($s)"; fi
+
+s=$(pigs procp $p)
+v=(${s// / })
+if [[ ${v[0]} = 1 && ${v[1]} = 1234 && ${v[2]} = 9876 ]]
+then echo "PROCP($p) ok"
+else echo "PROCP($p) fail ($s)"
+fi
+
+s=$(pigs procs $p)
+if [[ $s = 0 ]]; then echo "PROCS($p) ok"; else echo "PROCS($p) fail ($s)"; fi
+
+s=$(pigs procd $p)
+if [[ $s = 0 ]]; then echo "PROCD($p) ok"; else echo "PROCD($p) fail ($s)"; fi
+
+s=$(pigs prrg $GPIO)
+if [[ $s = 250 ]]; then echo "PRRG ok"; else echo "PRRG fail ($s)"; fi
+
+s=$(pigs prs $GPIO 1000)
+if [[ $s = 250 ]]; then echo "PRS-a ok"; else echo "PRS-a fail ($s)"; fi
+s=$(pigs prg $GPIO)
+if [[ $s = 1000 ]]; then echo "PRS-b ok"; else echo "PRS-b fail ($s)"; fi
+s=$(pigs prs $GPIO 255)
+if [[ $s = 250 ]]; then echo "PRS-c ok"; else echo "PRS-c fail ($s)"; fi
+s=$(pigs prg $GPIO)
+if [[ $s = 255 ]]; then echo "PRS-d ok"; else echo "PRS-d fail ($s)"; fi
+
+s=$(pigs pud $GPIO u)
+if [[ $s = "" ]]; then echo "PUD-a ok"; else echo "PUD-a fail ($s)"; fi
+s=$(pigs r $GPIO)
+if [[ $s = 1 ]]; then echo "PUD-b ok"; else echo "PUD-b fail ($s)"; fi
+s=$(pigs pud $GPIO d)
+if [[ $s = "" ]]; then echo "PUD-c ok"; else echo "PUD-c fail ($s)"; fi
+s=$(pigs r $GPIO)
+if [[ $s = 0 ]]; then echo "PUD-d ok"; else echo "PUD-d fail ($s)"; fi
+s=$(pigs pud $GPIO o)
+if [[ $s = "" ]]; then echo "PUD-e ok"; else echo "PUD-e fail ($s)"; fi
+
+s=$(pigs p $GPIO 128)
+if [[ $s = "" ]]; then echo "PWM-a ok"; else echo "PWM-a fail ($s)"; fi
+s=$(pigs pwm $GPIO 64)
+if [[ $s = "" ]]; then echo "PWM-b ok"; else echo "PWM-b fail ($s)"; fi
+s=$(pigs pwm $GPIO 0)
+if [[ $s = "" ]]; then echo "PWM-c ok"; else echo "PWM-c fail ($s)"; fi
+s=$(pigs m $GPIO r)
+if [[ $s = "" ]]; then echo "PWM-d ok"; else echo "PWM-d fail ($s)"; fi
+
+s=$(pigs pud $GPIO u)
+if [[ $s = "" ]]; then echo "READ-a ok"; else echo "READ-a fail ($s)"; fi
+s=$(pigs r $GPIO)
+if [[ $s = 1 ]]; then echo "READ-b ok"; else echo "READ-b fail ($s)"; fi
+s=$(pigs pud $GPIO d)
+if [[ $s = "" ]]; then echo "READ-c ok"; else echo "READ-c fail ($s)"; fi
+s=$(pigs read $GPIO)
+if [[ $s = 0 ]]; then echo "READ-d ok"; else echo "READ-d fail ($s)"; fi
+s=$(pigs pud $GPIO o)
+if [[ $s = "" ]]; then echo "READ-e ok"; else echo "READ-e fail ($s)"; fi
+
+s=$(pigs s $GPIO 500)
+if [[ $s = "" ]]; then echo "SERVO-a ok"; else echo "SERVO-a fail ($s)"; fi
+s=$(pigs servo $GPIO 2500)
+if [[ $s = "" ]]; then echo "SERVO-b ok"; else echo "SERVO-b fail ($s)"; fi
+s=$(pigs servo $GPIO 0)
+if [[ $s = "" ]]; then echo "SERVO-c ok"; else echo "SERVO-c fail ($s)"; fi
+s=$(pigs w $GPIO 1)
+if [[ $s = "" ]]; then echo "SERVO-d ok"; else echo "SERVO-d fail ($s)"; fi
+
+s=$(pigs wvclr)
+if [[ $s = 0 ]]; then echo "SLR-a ok"; else echo "SLR-a fail ($s)"; fi
+s=$(pigs slro $GPIO 1200)
+if [[ $s = 0 ]]; then echo "SLR-b ok"; else echo "SLR-b fail ($s)"; fi
+s=$(pigs wvas $GPIO 1200 0 "my name is joan")
+if [[ $s = 95 ]]; then echo "SLR-c ok"; else echo "SLR-c fail ($s)"; fi
+s=$(pigs m $GPIO w)
+if [[ $s = "" ]]; then echo "SLR-d ok"; else echo "SLR-d fail ($s)"; fi
+s=$(pigs wvgo)
+if [[ $s = 191 ]]; then echo "SLR-e ok"; else echo "SLR-e fail ($s)"; fi
+sleep 0.1
+s=$(pigs slr $GPIO 100)
+if [[ $s = "my name is joan" ]]
+then echo "SLR-f ok"
+else echo "SLR-f fail with [$s]"
+fi
+s=$(pigs slrc $GPIO)
+if [[ $s = 0 ]]; then echo "SLR-g ok"; else echo "SLR-g fail ($s)"; fi
+
+t1=$(pigs t)
+t2=$(pigs tick)
+s=$(($t2-$t1))
+if [[ $s -gt 0 && $s -lt 20000 ]]
+then echo "TICK ok"
+else echo "TICK fail($s)"
+fi
+
+pigs w $GPIO 1 # put in known state
+s=$(pigs trig $GPIO 10 1)
+if [[ $s = "" ]]; then echo "TRIG-a ok"; else echo "TRIG-a fail ($s)"; fi
+s=$(pigs r $GPIO)
+if [[ $s = 0 ]]; then echo "TRIG-b ok"; else echo "TRIG-b fail ($s)"; fi
+s=$(pigs trig $GPIO 10 0)
+if [[ $s = "" ]]; then echo "TRIG-c ok"; else echo "TRIG-c fail ($s)"; fi
+s=$(pigs r $GPIO)
+if [[ $s = 1 ]]; then echo "TRIG-d ok"; else echo "TRIG-d fail ($s)"; fi
+
+s=$(pigs wdog $GPIO 1000)
+if [[ $s = "" ]]; then echo "WDOG-a ok"; else echo "WDOG-a fail ($s)"; fi
+s=$(pigs wdog $GPIO 0)
+if [[ $s = "" ]]; then echo "WDOG-b ok"; else echo "WDOG-b fail ($s)"; fi
+
+s=$(pigs w $GPIO 1)
+if [[ $s = "" ]]; then echo "WRITE-a ok"; else echo "WRITE-a fail ($s)"; fi
+s=$(pigs r $GPIO)
+if [[ $s = 1 ]]; then echo "WRITE-b ok"; else echo "WRITE-b fail ($s)"; fi
+s=$(pigs write $GPIO 0)
+if [[ $s = "" ]]; then echo "WRITE-c ok"; else echo "WRITE-c fail ($s)"; fi
+s=$(pigs r $GPIO)
+if [[ $s = 0 ]]; then echo "WRITE-d ok"; else echo "WRITE-d fail ($s)"; fi
+
+
+s=$(pigs wvclr )
+if [[ $s = 0 ]]; then echo "WVCLR ok"; else echo "WVCLR fail ($s)"; fi
+s=$(pigs wvas $GPIO 300 0 "this is then winter of my discontent made glorious")
+if [[ $s = 309 ]]; then echo "WVAS ok"; else echo "WVAS fail ($s)"; fi
+s=$(pigs wvag 16 0 5000000 0 16 5000000)
+if [[ $s = 310 ]]; then echo "WVAG ok"; else echo "WVAG fail ($s)"; fi
+s=$(pigs wvgo)
+if [[ $s = 621 ]]; then echo "WVGO ok"; else echo "WVGO fail ($s)"; fi
+s=$(pigs wvbsy)
+if [[ $s = 1 ]]; then echo "WVBSY-a ok"; else echo "WVBSY-a fail ($s)"; fi
+sleep 1
+s=$(pigs wvbsy)
+if [[ $s = 1 ]]; then echo "WVBSY-b ok"; else echo "WVBSY-b fail ($s)"; fi
+s=$(pigs wvhlt)
+if [[ $s = 0 ]]; then echo "WVHLT ok"; else echo "WVHLT fail ($s)"; fi
+s=$(pigs wvbsy)
+if [[ $s = 0 ]]; then echo "WVBSY-c ok"; else echo "WVBSY-c fail ($s)"; fi
+s=$(pigs wvgor)
+if [[ $s = 621 ]]; then echo "WVGOR ok"; else echo "WVGOR fail ($s)"; fi
+s=$(pigs wvbsy)
+if [[ $s = 1 ]]; then echo "WVBSY-d ok"; else echo "WVBSY-d fail ($s)"; fi
+s=$(pigs wvhlt)
+if [[ $s = 0 ]]; then echo "WVHLT ok"; else echo "WVHLT fail ($s)"; fi
+s=$(pigs wvbsy)
+if [[ $s = 0 ]]; then echo "WVBSY-e ok"; else echo "WVBSY-e fail ($s)"; fi
+
+s=$(pigs wvsc 0)
+if [[ $s = 620 ]]; then echo "WVSC-a ok"; else echo "WVSC-a fail ($s)"; fi
+s=$(pigs wvsc 1)
+if [[ $s -ge 620 ]]; then echo "WVSC-b ok"; else echo "WVSC-b fail ($s)"; fi
+s=$(pigs wvsc 2)
+if [[ $s = 25016 ]]; then echo "WVSC-c ok"; else echo "WVSC-c fail ($s)"; fi
+
+s=$(pigs wvsm 0)
+if [[ $s = 10000000 ]]; then echo "WVSM-a ok"; else echo "WVSM-a fail ($s)"; fi
+s=$(pigs wvsm 1)
+if [[ $s -ge 10000000 ]]; then echo "WVSM-b ok"; else echo "WVSM-b fail ($s)"; fi
+s=$(pigs wvsm 2)
+if [[ $s = 1800000000 ]]
+then echo "WVSM-c ok"
+else echo "WVSM-c fail ($s)"
+fi
+
+s=$(pigs wvsp 0)
+if [[ $s = 310 ]]; then echo "WVSP-a ok"; else echo "WVSP-a fail ($s)"; fi
+s=$(pigs wvsp 1)
+if [[ $s -ge 310 ]]; then echo "WVSP-b ok"; else echo "WVSP-b fail ($s)"; fi
+s=$(pigs wvsp 2)
+if [[ $s = 12000 ]]; then echo "WVSP-c ok"; else echo "WVSP-c fail ($s)"; fi
+
diff --git a/x_pipe b/x_pipe
new file mode 100755 (executable)
index 0000000..16eb209
--- /dev/null
+++ b/x_pipe
@@ -0,0 +1,373 @@
+#!/bin/bash
+
+GPIO=4
+
+#
+# This script serves as a confidence check that the pipe interface to
+# the pigpio library is ok.
+#
+# The script uses gpio 4 (P1-7).  Make sure that nothing (or only a LED)
+# is connected to gpio 4 before running the script.
+#
+# To run the script
+# sudo pigpiod # if not already running
+# ./x_pipe # on the Pi running pigpiod
+#
+
+echo "bc1 0" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "BC1 ok"; else echo "BC1 fail ($s)"; fi
+
+echo "bc2 0" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "BC2 ok"; else echo "BC2 fail ($s)"; fi
+
+echo "br1" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ ${#s} = 8 ]]
+then echo "BR1 ok"
+else echo "BR1 fail ($s)"
+fi
+
+echo "br2" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ ${#s} = 8 ]]
+then echo "BR2 ok"
+else echo "BR2 fail ($s)"
+fi
+
+echo "bs1 0" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "BS1 ok"; else echo "BS1 fail ($s)"; fi
+
+echo "bs2 0" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "BS2 ok"; else echo "BS2 fail ($s)"; fi
+
+echo "h" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = "BC1 v         Clear gpios defined by mask v in bank 1." ]]
+then echo "HELP-a ok"
+else echo "HELP-a fail ($s)"
+fi
+read -t 1 -N 9000 </dev/pigout # dump rest of help
+echo "help" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = "BC1 v         Clear gpios defined by mask v in bank 1." ]]
+then echo "HELP-b ok"
+else echo "HELP-b fail ($s)"
+fi
+read -t 1 -N 9000 </dev/pigout # dump rest of help
+
+echo "hwver" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s -ne 0 ]]; then echo "HWVER ok"; else echo "HWVER fail ($s)"; fi
+
+echo "micro 1000" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "MICRO ok"; else echo "MICRO fail ($s)"; fi
+
+echo "milli 10" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "MILLI ok"; else echo "MILLI fail ($s)"; fi
+
+echo "modes $GPIO 0" >/dev/pigpio
+read -t 1 s </dev/pigout
+echo "modeg $GPIO" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 4 ]]; then echo "MODEG ok"; else echo "MODEG fail ($s)"; fi
+echo "m $GPIO r" >/dev/pigpio
+read -t 1 s </dev/pigout
+echo "mg $GPIO" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "MODES ok"; else echo "MODES fail ($s)"; fi
+
+echo "no" >/dev/pigpio
+read -t 1 h </dev/pigout
+if [[ $h -ge 0 && $h -le 31 ]]
+then echo "NO($h) ok"
+else echo "NO fail ($s)"
+fi
+
+echo "nb $h 0xabcd" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "NB($h) ok"; else echo "NB fail ($s)"; fi
+
+echo "np $h" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "NP($h) ok"; else echo "NP fail ($s)"; fi
+
+echo "nc $h" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "NC($h) ok"; else echo "NC fail ($s)"; fi
+
+echo "pfs $GPIO 800" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 800 ]]; then echo "PFG-a ok"; else echo "PFG-a fail ($s)"; fi
+
+echo "pfg $GPIO" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 800 ]]; then echo "PFG-b ok"; else echo "PFG-b fail ($s)"; fi
+
+echo "pfs $GPIO 0" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 10 ]]; then echo "PFS-a ok"; else echo "PFS-a fail ($s)"; fi
+
+echo "pfs $GPIO 800" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 800 ]]; then echo "PFS-b ok"; else echo "PFS-b fail ($s)"; fi
+
+echo "pigpv" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 13 ]]; then echo "PIGPV ok"; else echo "PIGPV fail ($s)"; fi
+
+echo "prs $GPIO 255" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 250 ]]; then echo "PRG-a ok"; else echo "PRG-a fail ($s)"; fi
+echo "prg $GPIO" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 255 ]]; then echo "PRG-b ok"; else echo "PRG-b fail ($s)"; fi
+
+echo "proc ldap 0 ldpa 1 ldai 29 ldpa 0 label 9 milli 1000 jmp 9" >/dev/pigpio
+read -t 1 p </dev/pigout
+if [[ $p -ge 0 && $p -le 31 ]]
+then echo "PROC($p) ok"
+else echo "PROC($p) fail ($s)"
+fi
+
+echo "procr $p 9876" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "PROCR($p) ok"; else echo "PROCR($p) fail ($s)"; fi
+
+echo "procp $p" >/dev/pigpio
+read -t 1 -a v </dev/pigout
+if [[ ${v[0]} = 1 && ${v[1]} = 29 && ${v[2]} = 9876 ]]
+then echo "PROCP($p) ok"
+else echo "PROCP($p) fail ($s)"
+fi
+
+echo "procs $p" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "PROCS($p) ok"; else echo "PROCS($p) fail ($s)"; fi
+
+echo "procd $p" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "PROCD($p) ok"; else echo "PROCD($p) fail ($s)"; fi
+
+echo "prrg $GPIO" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 250 ]]; then echo "PRRG ok"; else echo "PRRG fail ($s)"; fi
+
+echo "prs $GPIO 1000" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 250 ]]; then echo "PRS-a ok"; else echo "PRS-a fail ($s)"; fi
+echo "prg $GPIO" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 1000 ]]; then echo "PRS-b ok"; else echo "PRS-b fail ($s)"; fi
+echo "prs $GPIO 255" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 250 ]]; then echo "PRS-c ok"; else echo "PRS-c fail ($s)"; fi
+echo "prg $GPIO" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 255 ]]; then echo "PRS-d ok"; else echo "PRS-d fail ($s)"; fi
+
+echo "pud $GPIO u" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "PUD-a ok"; else echo "PUD-a fail ($s)"; fi
+echo "r $GPIO" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 1 ]]; then echo "PUD-b ok"; else echo "PUD-b fail ($s)"; fi
+echo "pud $GPIO d" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "PUD-c ok"; else echo "PUD-c fail ($s)"; fi
+echo "r $GPIO" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "PUD-d ok"; else echo "PUD-d fail ($s)"; fi
+echo "pud $GPIO o" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "PUD-e ok"; else echo "PUD-e fail ($s)"; fi
+
+echo "p $GPIO 128" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "PWM-a ok"; else echo "PWM-a fail ($s)"; fi
+echo "pwm $GPIO 64" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "PWM-b ok"; else echo "PWM-b fail ($s)"; fi
+echo "pwm $GPIO 0" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "PWM-c ok"; else echo "PWM-c fail ($s)"; fi
+echo "m $GPIO r" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "PWM-d ok"; else echo "PWM-d fail ($s)"; fi
+
+echo "pud $GPIO u" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "READ-a ok"; else echo "READ-a fail ($s)"; fi
+echo "r $GPIO" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 1 ]]; then echo "READ-b ok"; else echo "READ-b fail ($s)"; fi
+echo "pud $GPIO d" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "READ-c ok"; else echo "READ-c fail ($s)"; fi
+echo "read $GPIO" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "READ-d ok"; else echo "READ-d fail ($s)"; fi
+echo "pud $GPIO o" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "READ-e ok"; else echo "READ-e fail ($s)"; fi
+
+echo "s $GPIO 500" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "SERVO-a ok"; else echo "SERVO-a fail ($s)"; fi
+echo "servo $GPIO 2500" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "SERVO-b ok"; else echo "SERVO-b fail ($s)"; fi
+echo "servo $GPIO 0" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "SERVO-c ok"; else echo "SERVO-c fail ($s)"; fi
+echo "w $GPIO 1" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "SERVO-d ok"; else echo "SERVO-d fail ($s)"; fi
+
+echo "wvclr" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "SLR-a ok"; else echo "SLR-a fail ($s)"; fi
+echo "slro $GPIO 1200" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "SLR-b ok"; else echo "SLR-b fail ($s)"; fi
+echo "wvas $GPIO 1200 0 my name is joan¬" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 105 ]]; then echo "SLR-c ok"; else echo "SLR-c fail ($s)"; fi
+echo "m $GPIO w" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "SLR-d ok"; else echo "SLR-d fail ($s)"; fi
+echo "wvgo" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 211 ]]; then echo "SLR-e ok"; else echo "SLR-e fail ($s)"; fi
+sleep 0.2
+echo "slr $GPIO 100" >/dev/pigpio
+read -t 1 -d ¬ s </dev/pigout
+if [[ $s = "my name is joan" ]]
+then echo "SLR-f ok"
+else echo "SLR-f fail with ($s)"
+fi
+read -t 1 s </dev/pigout # dump any junk
+echo "slrc $GPIO" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "SLR-g ok"; else echo "SLR-g fail ($s)"; fi
+
+echo "t" >/dev/pigpio
+echo "tick" >/dev/pigpio
+read -t 1 t1 </dev/pigout
+read -t 1 t2 </dev/pigout
+s=$((t2-t1))
+if [[ $s -gt 0 && $s -lt 5000 ]]
+then echo "TICK ok"
+else echo "TICK fail ($s)"
+fi
+
+pigs w $GPIO 1 # put in known state
+echo "trig $GPIO 10 1" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "TRIG-a ok"; else echo "TRIG-a fail ($s)"; fi
+echo "r $GPIO" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "TRIG-b ok"; else echo "TRIG-b fail ($s)"; fi
+echo "trig $GPIO 10 0" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "TRIG-c ok"; else echo "TRIG-c fail ($s)"; fi
+echo "r $GPIO" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 1 ]]; then echo "TRIG-d ok"; else echo "TRIG-d fail ($s)"; fi
+
+echo "wdog $GPIO 1000" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "WDOG-a ok"; else echo "WDOG-a fail ($s)"; fi
+echo "wdog $GPIO 0" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "WDOG-b ok"; else echo "WDOG-b fail ($s)"; fi
+
+echo "w $GPIO 1" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "WRITE-a ok"; else echo "WRITE-a fail ($s)"; fi
+echo "r $GPIO" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 1 ]]; then echo "WRITE-b ok"; else echo "WRITE-b fail ($s)"; fi
+echo "write $GPIO 0" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "WRITE-c ok"; else echo "WRITE-c fail ($s)"; fi
+echo "r $GPIO" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "WRITE-d ok"; else echo "WRITE-d fail ($s)"; fi
+
+echo "wvclr" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "WVCLR ok"; else echo "WVCLR fail ($s)"; fi
+echo "wvas $GPIO 300 0 this is the winter of my discontent" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 215 ]]; then echo "WVAS ok"; else echo "WVAS fail ($s)"; fi
+echo "wvag 16 0 5000000 0 16 5000000" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 216 ]]; then echo "WVAG ok"; else echo "WVAG fail ($s)"; fi
+echo "wvgo" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 433 ]]; then echo "WVGO ok"; else echo "WVGO fail ($s)"; fi
+echo "wvbsy" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 1 ]]; then echo "WVBSY-a ok"; else echo "WVBSY-a fail ($s)"; fi
+sleep 1
+echo "wvbsy" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 1 ]]; then echo "WVBSY-b ok"; else echo "WVBSY-b fail ($s)"; fi
+echo "wvhlt" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "WVHLT ok"; else echo "WVHLT fail ($s)"; fi
+echo "wvbsy" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "WVBSY-c ok"; else echo "WVBSY-c fail ($s)"; fi
+echo "wvgor" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 433 ]]; then echo "WVGOR ok"; else echo "WVGOR fail ($s)"; fi
+echo "wvbsy" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 1 ]]; then echo "WVBSY-d ok"; else echo "WVBSY-d fail ($s)"; fi
+echo "wvhlt" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "WVHLT ok"; else echo "WVHLT fail ($s)"; fi
+echo "wvbsy" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 0 ]]; then echo "WVBSY-e ok"; else echo "WVBSY-e fail ($s)"; fi
+
+echo "wvsc 0" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 432 ]]; then echo "WVSC-a ok"; else echo "WVSC-a fail ($s)"; fi
+echo "wvsc 1" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s -ge 432 ]]; then echo "WVSC-b ok"; else echo "WVSC-b fail ($s)"; fi
+echo "wvsc 2" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 25016 ]]; then echo "WVSC-c ok"; else echo "WVSC-c fail ($s)"; fi
+
+echo "wvsm 0" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 10000000 ]]; then echo "WVSM-a ok"; else echo "WVSM-a fail ($s)"; fi
+echo "wvsm 1" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s -ge 10000000 ]]; then echo "WVSM-b ok"; else echo "WVSM-b fail ($s)"; fi
+echo "wvsm 2" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 1800000000 ]]
+then echo "WVSM-c ok"
+else echo "WVSM-c fail ($s)"
+fi
+
+echo "wvsp 0" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 216 ]]; then echo "WVSP-a ok"; else echo "WVSP-a fail ($s)"; fi
+echo "wvsp 1" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s -ge 216 ]]; then echo "WVSP-b ok"; else echo "WVSP-b fail ($s)"; fi
+echo "wvsp 2" >/dev/pigpio
+read -t 1 s </dev/pigout
+if [[ $s = 12000 ]]; then echo "WVSP-c ok"; else echo "WVSP-c fail ($s)"; fi
+