V45
authorjoan <joan@abyz.co.uk>
Sat, 13 Feb 2016 14:42:41 +0000 (14:42 +0000)
committerjoan <joan@abyz.co.uk>
Sat, 13 Feb 2016 14:42:41 +0000 (14:42 +0000)
pigpio.c
pigpio.h
x_pigpio.c
x_pigpio.py
x_pigpiod_if.c
x_pigpiod_if2.c

index 05bde0d7c284ed5102bbdd411a25805a09fcb81e..d7625f7a245a97a242b7ededc5e5eb3eea356104 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 44 */
+/* pigpio version 45 */
 
 /* include ------------------------------------------------------- */
 
@@ -742,7 +742,8 @@ Assumes two counters per block.  Each counter 4 * 16 (16^4=65536)
 
 #define PI_WF_MICROS   1
 
-#define DATUMS 125
+#define MAX_REPORT 120
+#define MAX_SAMPLE 4000
 
 #define DEFAULT_PWM_IDX 5
 
@@ -1174,10 +1175,7 @@ static volatile uint32_t wdogBits    = 0;
 
 static volatile int runState = PI_STARTING;
 
-static int triggerAlert2 = 0;
-
-static int pthAlert1Running  = 0;
-static int pthAlert2Running  = 0;
+static int pthAlertRunning  = 0;
 static int pthFifoRunning   = 0;
 static int pthSocketRunning = 0;
 
@@ -1215,8 +1213,6 @@ static int fdMem        = -1;
 static int fdSock       = -1;
 static int fdPmap       = -1;
 static int fdMbox       = -1;
-static int fdAlertRead  = -1;
-static int fdAlertWrite = -1;
 
 static DMAMem_t *dmaMboxBlk = MAP_FAILED;
 static uintptr_t * * dmaPMapBlk = MAP_FAILED;
@@ -1266,13 +1262,10 @@ static volatile gpioCfg_t gpioCfg =
 static unsigned bufferBlocks; /* number of blocks in buffer */
 static unsigned bufferCycles; /* number of cycles */
 
-static pthread_t pthAlert1;
-static pthread_t pthAlert2;
+static pthread_t pthAlert;
 static pthread_t pthFifo;
 static pthread_t pthSocket;
 
-static gpioReport_t gpioReport[DATUMS];
-
 static uint32_t spi_dummy;
 
 static unsigned old_mode_ce0;
@@ -5108,10 +5101,30 @@ static void sigSetHandler(void)
    }
 }
 
+/*
+   freq mics  net
+ 0 1000 1000  900
+ 1 4000  250  225
+ 2 3750  266  240
+ 3 3500  285  257
+ 4 3250  307  276
+ 5 3000  333  300
+ 6 2750  363  327
+ 7 2500  400  360
+ 8 2250  444  400
+ 9 2000  500  450
+10 1750  571  514
+11 1500  666  600
+12 1250  800  720
+13 1000 1000  900
+14 750  1333 1200
+15 500  2000 1800
+*/
+
 unsigned alert_delays[]=
 {
-   1000000, 1034000, 1071000, 1111000, 1154000, 1200000, 1250000, 1304000,
-   1364000, 1429000, 1500000, 1579000, 1667000, 1765000, 1875000, 2000000
+   900000, 225000, 240000, 257142, 276923, 300000,  327272,  360000,
+   400000, 450000, 514285, 600000, 720000, 900000, 1200000, 1800000
 };
 
 /* ======================================================================= */
@@ -5119,7 +5132,7 @@ unsigned alert_delays[]=
 static void alertGlitchFilter(gpioSample_t *sample, int numSamples)
 {
    int i, j, diff;
-   uint32_t steadyUs, nowTick, RBitV, LBitV;
+   uint32_t steadyUs, changedTick, RBitV, LBitV;
    uint32_t bit, bitV;
 
    for (i=0; i<=PI_MAX_USER_GPIO; i++)
@@ -5128,20 +5141,29 @@ static void alertGlitchFilter(gpioSample_t *sample, int numSamples)
 
       if (monitorBits & bit & gFilterBits)
       {
-         steadyUs = gpioAlert[i].gfSteadyUs;
-         RBitV    = gpioAlert[i].gfRBitV;
-         LBitV    = gpioAlert[i].gfLBitV;
-         nowTick  = gpioAlert[i].gfTick;
+         steadyUs    = gpioAlert[i].gfSteadyUs;
+         RBitV       = gpioAlert[i].gfRBitV;
+         LBitV       = gpioAlert[i].gfLBitV;
+         changedTick = gpioAlert[i].gfTick;
 
          for (j=0; j<numSamples; j++)
          {
             bitV = sample[j].level & bit;
 
+            if (bitV != LBitV)
+            {
+               /* Difference between level and last level.
+                  Restart steady timer. */
+
+               changedTick = sample[j].tick;
+               LBitV = bitV;
+            }
+
             if (bitV != RBitV)
             {
                /* Difference between level and reported level. */
 
-               diff = sample[j].tick - nowTick;
+               diff = sample[j].tick - changedTick;
 
                if (diff >= steadyUs)
                {
@@ -5156,19 +5178,11 @@ static void alertGlitchFilter(gpioSample_t *sample, int numSamples)
                }
             }
 
-            if (bitV != LBitV)
-            {
-               /* Difference between level and last level.
-                  Restart steady timer. */
-
-               nowTick = sample[j].tick;
-               LBitV = bitV;
-            }
          }
 
          gpioAlert[i].gfRBitV = RBitV;
          gpioAlert[i].gfLBitV = LBitV;
-         gpioAlert[i].gfTick  = nowTick;
+         gpioAlert[i].gfTick  = changedTick;
       }
    }
 }
@@ -5239,16 +5253,351 @@ static void alertNoiseFilter(gpioSample_t *sample, int numSamples)
    }
 }
 
-static void alertWdogCheck(gpioSample_t *sample, int numSamples, uint32_t tick)
+uint32_t _reportedLevel, _changedBits;
+
+static void alertEmit(gpioSample_t *sample, int numSamples, uint32_t eTick)
+{
+   uint32_t oldLevel, newLevel;
+   int32_t diff;
+   int emit, seqno, emitted;
+   uint32_t changes, bits, timeoutBits;
+   int d;
+   int b, n, v;
+   int err;
+   int max_emits;
+   char fifo[32];
+   gpioReport_t report[MAX_REPORT];
+
+   if (_changedBits)
+   {
+      if (gpioGetSamples.func)
+      {
+         if (gpioGetSamples.ex)
+         {
+            (gpioGetSamples.func)
+               (sample, numSamples, gpioGetSamples.userdata);
+         }
+         else
+         {
+            (gpioGetSamples.func)(sample, numSamples);
+         }
+      }
+   }
+
+   /* call alert callbacks for each bit transition */
+
+   if (_changedBits & alertBits)
+   {
+      oldLevel = (_reportedLevel & alertBits);
+
+      for (d=0; d<numSamples; d++)
+      {
+         newLevel = (sample[d].level & alertBits);
+
+         if (newLevel != oldLevel)
+         {
+            changes = (newLevel ^ oldLevel);
+
+            for (b=0; b<=PI_MAX_USER_GPIO; b++)
+            {
+               if (changes & (1<<b))
+               {
+                  if (newLevel & (1<<b)) v = 1; else v = 0;
+
+                  if (gpioAlert[b].func)
+                  {
+                     if (gpioAlert[b].ex)
+                     {
+                        (gpioAlert[b].func)
+                           (b, v, sample[d].tick,
+                            gpioAlert[b].userdata);
+                     }
+                     else
+                     {
+                        (gpioAlert[b].func)(b, v, sample[d].tick);
+                     }
+                  }
+               }
+            }
+            oldLevel = newLevel;
+         }
+      }
+   }
+
+   /* check for watchdog timeouts */
+
+   timeoutBits = 0;
+
+   for (b=0; b<=PI_MAX_USER_GPIO; b++)
+   {
+      if (gpioAlert[b].wdSteadyUs)
+      {
+         diff = eTick - gpioAlert[b].wdTick;
+
+         if (diff >= gpioAlert[b].wdSteadyUs)
+         {
+            timeoutBits |= (1<<b);
+
+            gpioAlert[b].wdTick = eTick;
+
+            if (gpioAlert[b].func)
+            {
+               if (gpioAlert[b].ex)
+               {
+                  (gpioAlert[b].func)
+                     (b, PI_TIMEOUT, eTick, gpioAlert[b].userdata);
+               }
+               else
+               {
+                  (gpioAlert[b].func)(b, PI_TIMEOUT, eTick);
+               }
+            }
+         }
+      }
+   }
+
+   for (n=0; n<PI_NOTIFY_SLOTS; n++)
+   {
+      if (gpioNotify[n].state == PI_NOTIFY_CLOSING)
+      {
+
+         if (gpioNotify[n].pipe)
+         {
+            DBG(DBG_INTERNAL, "close notify pipe %d", gpioNotify[n].fd);
+            close(gpioNotify[n].fd);
+
+            sprintf(fifo, "/dev/pigpio%d", n);
+
+            unlink(fifo);
+         }
+
+         gpioNotify[n].state = PI_NOTIFY_CLOSED;
+
+      }
+      else if (gpioNotify[n].state == PI_NOTIFY_RUNNING)
+      {
+         bits = gpioNotify[n].bits;
+
+         emit = 0;
+
+         seqno = gpioNotify[n].seqno;
+
+         /* check to see if any bits have changed for this
+            notification.
+
+            bits         is the set of notification bits
+            _changedBits is the set of changed bits
+         */
+
+         if (_changedBits & bits)
+         {
+            oldLevel = _reportedLevel & bits;
+
+            for (d=0; d<numSamples; d++)
+            {
+               newLevel = sample[d].level & bits;
+
+               if (newLevel != oldLevel)
+               {
+                  report[emit].seqno = seqno;
+                  report[emit].flags = 0;
+                  report[emit].tick  = sample[d].tick;
+                  report[emit].level = sample[d].level;
+
+                  oldLevel = newLevel;
+
+                  emit++;
+                  seqno++;
+               }
+            }
+         }
+
+         /* check to see if any watchdogs are due for this
+            notification.
+
+            bits        is the set of notification bits
+            timeoutBits is the set of timed out bits
+         */
+
+         bits = gpioNotify[n].bits;
+
+         if (timeoutBits & bits)
+         {
+            /* at least one watchdog has fired for this
+               notification.
+            */
+
+            for (b=0; b<=PI_MAX_USER_GPIO; b++)
+            {
+               if (timeoutBits & bits & (1<<b))
+               {
+                  if (numSamples)
+                     newLevel = sample[numSamples-1].level;
+                  else
+                     newLevel = _reportedLevel;
+
+                  report[emit].seqno = seqno;
+                  report[emit].flags = PI_NTFY_FLAGS_WDOG |
+                                           PI_NTFY_FLAGS_BIT(b);
+                  report[emit].tick  = eTick;
+                  report[emit].level = newLevel;
+
+                  emit++;
+                  seqno++;
+               }
+            }
+         }
+
+         if (!emit)
+         {
+            if ((eTick - gpioNotify[n].lastReportTick) > 60000000)
+            {
+               if (numSamples)
+                  newLevel = sample[numSamples-1].level;
+               else
+                  newLevel = _reportedLevel;
+
+               report[emit].seqno = seqno;
+               report[emit].flags = PI_NTFY_FLAGS_ALIVE;
+               report[emit].tick  = eTick;
+               report[emit].level = newLevel;
+
+               emit++;
+               seqno++;
+            }
+         }
+
+         if (emit)
+         {
+            DBG(DBG_FAST_TICK, "notification %d (%d reports, %x-%x)",
+               n, emit, report[0].seqno,  report[emit-1].seqno);
+            gpioNotify[n].lastReportTick = eTick;
+            max_emits = gpioNotify[n].max_emits;
+
+            if (emit > gpioStats.maxEmit) gpioStats.maxEmit = emit;
+
+            emitted = 0;
+
+            while (emit > 0)
+            {
+               if (emit > max_emits)
+               {
+                  gpioStats.emitFrags++;
+
+                  err = write(gpioNotify[n].fd,
+                           report+emitted,
+                           max_emits*sizeof(gpioReport_t));
+
+                  if (err != (max_emits*sizeof(gpioReport_t)))
+                  {
+                     if (err < 0)
+                     {
+                        if ((errno != EAGAIN) && (errno != EWOULDBLOCK))
+                        {
+                           /* serious error, no point continuing */
+
+                           DBG(DBG_ALWAYS, "fd=%d err=%d errno=%d",
+                              gpioNotify[n].fd, err, errno);
+
+                           DBG(DBG_ALWAYS, "%s", strerror(errno));
+
+                           gpioNotify[n].bits  = 0;
+                           gpioNotify[n].state = PI_NOTIFY_CLOSING;
+                           intNotifyBits();
+                           break;
+                        }
+                        else gpioStats.wouldBlockPipeWrite++;
+                     }
+                     else
+                     {
+                        gpioCfg.internals |= PI_CFG_STATS;
+                        gpioStats.shortPipeWrite++;
+                        DBG(DBG_ALWAYS, "emitted %d, asked for %d",
+                           err/sizeof(gpioReport_t), max_emits);
+                     }
+                  }
+                  else gpioStats.goodPipeWrite++;
+
+                  emitted += max_emits;
+                  emit    -= max_emits;
+               }
+               else
+               {
+                  err = write(gpioNotify[n].fd,
+                           report+emitted,
+                           emit*sizeof(gpioReport_t));
+
+                  if (err != (emit*sizeof(gpioReport_t)))
+                  {
+                     if (err < 0)
+                     {
+                        if ((errno != EAGAIN) && (errno != EWOULDBLOCK))
+                        {
+                           DBG(DBG_ALWAYS, "fd=%d err=%d errno=%d",
+                              gpioNotify[n].fd, err, errno);
+
+                           DBG(DBG_ALWAYS, "%s", strerror(errno));
+
+                           /* serious error, no point continuing */
+                           gpioNotify[n].bits  = 0;
+                           gpioNotify[n].state = PI_NOTIFY_CLOSING;
+                           intNotifyBits();
+                           break;
+                        }
+                        else gpioStats.wouldBlockPipeWrite++;
+                     }
+                     else
+                     {
+                        gpioCfg.internals |= PI_CFG_STATS;
+                        gpioStats.shortPipeWrite++;
+                        DBG(DBG_ALWAYS, "emitted %d, asked for %d",
+                           err/sizeof(gpioReport_t), emit);
+                     }
+                  }
+                  else gpioStats.goodPipeWrite++;
+
+                  emitted += emit;
+                  emit = 0;
+               }
+            }
+
+            gpioNotify[n].seqno = seqno;
+         }
+      }
+   }
+
+   if (_changedBits & scriptBits)
+   {
+      for (n=0; n<PI_MAX_SCRIPTS; 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);
+         }
+      }
+   }
+
+   if (numSamples) _reportedLevel = sample[numSamples-1].level;
+}
+
+static void alertWdogCheck(gpioSample_t *sample, int numSamples)
 {
    /*
    Go through and set the last time each GPIO with a watchdog changed state.
-
-   If the watchdog has expired make sure the alert 2 thread is called to
-   emit the watchdog report.
    */
 
-   int i, j, diff;
+   int i, j;
    uint32_t LBitV;
    uint32_t bit;
 
@@ -5270,39 +5619,23 @@ static void alertWdogCheck(gpioSample_t *sample, int numSamples, uint32_t tick)
          }
 
          gpioAlert[i].wdLBitV = LBitV;
-
-         diff = tick - gpioAlert[i].wdTick;
-
-         if (diff >= gpioAlert[i].wdSteadyUs) triggerAlert2 = 1;
       }
    }
 }
 
-#define PIPE_MAX 500
-
-static void * pthAlert1Thread(void *x)
+static void * pthAlertThread(void *x)
 {
    struct timespec req, rem;
-   uint32_t oldLevel, newLevel, level, reportedLevel;
+   uint32_t oldLevel, newLevel, level;
    uint32_t oldSlot,  newSlot;
-   uint32_t stick, expected, ft;
+   uint32_t expected, ft, sTick;
    int32_t diff, minDiff, stickInited;
    int cycle, pulse;
-   uint32_t changedBits;
    int numSamples, ticks, i;
-   int rp, compactedSamples;
+   int rp, compactedSamples, totalSamples;
    int stopped;
    int moreToDo;
-   int skippedAlert2Count;
-   gpioSample_t sample[PIPE_MAX];
-   int pipefd[2];
-
-   pipe(pipefd);
-
-   fdAlertRead  = pipefd[0];
-   fdAlertWrite = pipefd[1];
-
-   i = fcntl(fdAlertRead, F_SETPIPE_SZ, 32*4096);
+   gpioSample_t sample[MAX_SAMPLE];
 
    req.tv_sec = 0;
 
@@ -5310,7 +5643,7 @@ static void * pthAlert1Thread(void *x)
 
    spinWhileStarting();
 
-   reportedLevel = gpioReg[GPLEV0];
+   _reportedLevel = gpioReg[GPLEV0];
 
    oldSlot = dmaCurrentSlot(dmaNowAtICB());
 
@@ -5326,39 +5659,63 @@ static void * pthAlert1Thread(void *x)
 
    stickInited = 0;
 
-   stick = 0;
+   sTick = 0;
 
    minDiff = gpioCfg.clockMicros / 2;
 
-   skippedAlert2Count = 0;
-
    while (1)
    {
+      /* Check that DMA is running okay */
+
+      if (dmaIn[DMA_CONBLK_AD])
+      {
+         if (stopped)
+         {
+            DBG(DBG_STARTUP, "****** GOING ******");
+            stopped = 0;
+         }
+      }
+      else
+      {
+         stopped = 1;
+
+         myGpioDelay(5000);
+
+         if (runState == PI_RUNNING)
+         {
+            /* should never be executed, leave code just in case */
+
+            gpioCfg.internals |= PI_CFG_STATS;
+
+            dmaInitCbs();
+            flushMemory();
+            initDMAgo((uint32_t *)dmaIn, (uint32_t)dmaIBus[0]);
+            myGpioDelay(5000); /* let DMA run for a while */
+            oldSlot = dmaCurrentSlot(dmaNowAtICB());
+            gpioStats.DMARestarts++;
+         }
+      }
+
       newSlot = dmaCurrentSlot(dmaNowAtICB());
 
       newSlot = (newSlot / PULSE_PER_CYCLE) * PULSE_PER_CYCLE;
 
       numSamples = 0;
 
-      changedBits = 0;
-
-      oldLevel = reportedLevel & monitorBits;
-
       /*
-      Work through latest samples saving any level
-      changes of gpios of interest.
+      Extract samples from DMA ring buffer.
       */
 
-      while ((oldSlot != newSlot) && (numSamples < PIPE_MAX))
+      while ((oldSlot != newSlot) && (numSamples < MAX_SAMPLE))
       {
          level = myGetLevel(oldSlot++);
 
-         sample[numSamples].tick  = stick;
+         sample[numSamples].tick  = sTick;
          sample[numSamples].level = level;
 
          numSamples++;
 
-         stick += gpioCfg.clockMicros;
+         sTick += gpioCfg.clockMicros;
 
          if (++pulse >= PULSE_PER_CYCLE)
          {
@@ -5370,19 +5727,19 @@ static void * pthAlert1Thread(void *x)
                oldSlot = 0;
             }
 
-            expected = stick;
+            expected = sTick;
 
-            stick = myGetTick(cycle);
+            sTick = myGetTick(cycle);
 
             if (stickInited)
             {
-               diff = stick - expected;
+               diff = sTick - expected;
 
                if (abs(diff) > minDiff)
                {
                   ft = sample[numSamples-PULSE_PER_CYCLE].tick;
 
-                  ticks = stick - ft;
+                  ticks = sTick - ft;
 
                   for (i=1; i<PULSE_PER_CYCLE; i++)
                   {
@@ -5425,7 +5782,11 @@ static void * pthAlert1Thread(void *x)
 
       /* Compact samples */
 
+      _changedBits = 0;
+      oldLevel = _reportedLevel & monitorBits;
+
       compactedSamples = 0;
+      totalSamples = 0;
 
       for (rp=0; rp<numSamples; rp++)
       {
@@ -5437,483 +5798,58 @@ static void * pthAlert1Thread(void *x)
          {
             sample[compactedSamples].tick  = sample[rp].tick;
             sample[compactedSamples].level = level;
-            compactedSamples++;
-
-            changedBits |= (newLevel ^ oldLevel);
-
+            _changedBits |= (newLevel ^ oldLevel);
             oldLevel = newLevel;
-         }
-      }
 
-      if (++skippedAlert2Count > 10000)
-      {
-         /* call alert 2 thread at least every 10 seconds */
+            compactedSamples++;
+            if (compactedSamples >= MAX_REPORT)
+            {
+               totalSamples += compactedSamples;
 
-         triggerAlert2 = 1;
-      }
+               /* Rebase watchdog timeouts */
+               if (wdogBits) alertWdogCheck(sample, compactedSamples);
 
-      if (compactedSamples)
-      {
-         numSamples = compactedSamples;
-         if (wdogBits)
-            alertWdogCheck(sample, numSamples, sample[numSamples-1].tick);
-      }
-      else
-      {
-         if (wdogBits) alertWdogCheck(sample, 0, sample[numSamples-1].tick);
+               gpioStats.numSamples += compactedSamples;
 
-         if (triggerAlert2)
-         {
-            /* emit a null report to trigger alert 2 thread */
+               alertEmit(sample, compactedSamples, sample[rp].tick);
 
-            sample[0].tick  = sample[numSamples-1].tick;
-            sample[0].level = sample[numSamples-1].level;
-            numSamples = 1;
+               compactedSamples = 0;
+            }
          }
-         else numSamples = 0;
       }
 
-      if (numSamples)
+      if (compactedSamples)
       {
-         write(fdAlertWrite, sample, numSamples * sizeof(gpioSample_t));
+         totalSamples += compactedSamples;
 
-         skippedAlert2Count = 0;
-
-         /* once all outputs have been emitted set reported level */
-
-         reportedLevel = sample[numSamples-1].level;
-
-         if (compactedSamples > gpioStats.maxSamples)
-            gpioStats.maxSamples = compactedSamples;
+         /* Rebase watchdog timeouts */
+         if (wdogBits) alertWdogCheck(sample, compactedSamples);
 
          gpioStats.numSamples += compactedSamples;
       }
 
-      /* Check that DMA is running okay */
-
-      if (dmaIn[DMA_CONBLK_AD])
-      {
-         if (stopped)
-         {
-            DBG(DBG_STARTUP, "****** GOING ******");
-            stopped = 0;
-         }
-      }
-      else
-      {
-         stopped = 1;
-
-         myGpioDelay(5000);
-
-         if (runState == PI_RUNNING)
-         {
-            /* should never be executed, leave code just in case */
-
-            gpioCfg.internals |= PI_CFG_STATS;
+      alertEmit(sample, compactedSamples, sTick);
 
-            dmaInitCbs();
-            flushMemory();
-            initDMAgo((uint32_t *)dmaIn, (uint32_t)dmaIBus[0]);
-            myGpioDelay(5000); /* let DMA run for a while */
-            oldSlot = dmaCurrentSlot(dmaNowAtICB());
-            gpioStats.DMARestarts++;
-         }
-      }
+      if (totalSamples > gpioStats.maxSamples)
+         gpioStats.maxSamples = numSamples;
 
       req.tv_sec = 0;
       req.tv_nsec = alert_delays[(gpioCfg.internals>>PI_CFG_ALERT_FREQ)&15];
 
       if (moreToDo)
       {
-         req.tv_nsec /= 2;
-
          gpioStats.moreToDo++;
       }
       else
       {
          gpioStats.alertTicks++;
-      }
-
-      while (nanosleep(&req, &rem))
-      {
-         req.tv_sec  = rem.tv_sec;
-         req.tv_nsec = rem.tv_nsec;
-      }
-   }
-
-   return 0;
-}
-
-static void * pthAlert2Thread(void *x)
-{
-   uint32_t oldLevel, newLevel, reportedLevel;
-   uint32_t stick;
-   int32_t diff;
-   int emit, seqno, emitted;
-   uint32_t changes, bits, changedBits, timeoutBits;
-   int numSamples, d, i;
-   int b, n, v;
-   int err;
-   int max_emits;
-   int bytes;
-   char fifo[32];
-   gpioSample_t sample[DATUMS];
-
-   while (fdAlertRead < 0) time_sleep(0.01) ;
-
-   reportedLevel = gpioReg[GPLEV0];
-
-   oldLevel = reportedLevel & monitorBits;
-
-   while (1)
-   {
-      bytes = read(fdAlertRead, sample, DATUMS * sizeof(gpioSample_t));
-
-      triggerAlert2 = 0;
-
-      numSamples = bytes / sizeof(gpioSample_t);
-
-      changedBits = 0;
-
-      stick = sample[numSamples-1].tick;
-
-      for (i=0; i<numSamples; i++)
-      {
-         newLevel = (sample[i].level & monitorBits);
-
-         if (newLevel != oldLevel)
-         {
-            changedBits |= (newLevel ^ oldLevel);
-            oldLevel = newLevel;
-         }
-      }
-
-      if (changedBits)
-      {
-         if (gpioGetSamples.func)
-         {
-            if (gpioGetSamples.ex)
-            {
-               (gpioGetSamples.func)
-                  (sample, numSamples, gpioGetSamples.userdata);
-            }
-            else
-            {
-               (gpioGetSamples.func)
-                  (sample, numSamples);
-            }
-         }
-      }
-
-      /* call alert callbacks for each bit transition */
-
-      if (changedBits & alertBits)
-      {
-         oldLevel = (reportedLevel & alertBits);
-
-         for (d=0; d<numSamples; d++)
-         {
-            newLevel = (sample[d].level & alertBits);
-
-            if (newLevel != oldLevel)
-            {
-               changes = (newLevel ^ oldLevel);
-
-               for (b=0; b<=PI_MAX_USER_GPIO; b++)
-               {
-                  if (changes & (1<<b))
-                  {
-                     if (newLevel & (1<<b)) v = 1; else v = 0;
-
-                     if (gpioAlert[b].func)
-                     {
-                        if (gpioAlert[b].ex)
-                        {
-                           (gpioAlert[b].func)
-                              (b, v, sample[d].tick,
-                               gpioAlert[b].userdata);
-                        }
-                        else
-                        {
-                           (gpioAlert[b].func)
-                              (b, v, sample[d].tick);
-                        }
-                     }
-                  }
-               }
-               oldLevel = newLevel;
-            }
-         }
-      }
-
-      /* check for watchdog timeouts */
-
-      timeoutBits = 0;
-
-      for (b=0; b<=PI_MAX_USER_GPIO; b++)
-      {
-         if (gpioAlert[b].wdSteadyUs)
-         {
-            diff = stick - gpioAlert[b].wdTick;
-
-            if (diff >= gpioAlert[b].wdSteadyUs)
-            {
-               timeoutBits |= (1<<b);
-
-               gpioAlert[b].wdTick += gpioAlert[b].wdSteadyUs;
 
-               if (gpioAlert[b].func)
-               {
-                  if (gpioAlert[b].ex)
-                  {
-                     (gpioAlert[b].func)
-                        (b, PI_TIMEOUT, stick, gpioAlert[b].userdata);
-                  }
-                  else
-                  {
-                     (gpioAlert[b].func)
-                        (b, PI_TIMEOUT, stick);
-                  }
-               }
-            }
-         }
-      }
-
-      for (n=0; n<PI_NOTIFY_SLOTS; n++)
-      {
-         if (gpioNotify[n].state == PI_NOTIFY_CLOSING)
+         while (nanosleep(&req, &rem))
          {
-
-            if (gpioNotify[n].pipe)
-            {
-               DBG(DBG_INTERNAL, "close notify pipe %d", gpioNotify[n].fd);
-               close(gpioNotify[n].fd);
-
-               sprintf(fifo, "/dev/pigpio%d", n);
-
-               unlink(fifo);
-            }
-
-            gpioNotify[n].state = PI_NOTIFY_CLOSED;
-
-         }
-         else if (gpioNotify[n].state == PI_NOTIFY_RUNNING)
-         {
-            bits = gpioNotify[n].bits;
-
-            emit = 0;
-
-            seqno = gpioNotify[n].seqno;
-
-            /* check to see if any bits have changed for this
-               notification.
-
-               bits        is the set of notification bits
-               changedBits is the set of changed bits
-            */
-
-            if (changedBits & bits)
-            {
-               oldLevel = reportedLevel & bits;
-
-               for (d=0; d<numSamples; d++)
-               {
-                  newLevel = sample[d].level & bits;
-
-                  if (newLevel != oldLevel)
-                  {
-                     gpioReport[emit].seqno = seqno;
-                     gpioReport[emit].flags = 0;
-                     gpioReport[emit].tick  = sample[d].tick;
-                     gpioReport[emit].level = sample[d].level;
-
-                     oldLevel = newLevel;
-
-                     emit++;
-                     seqno++;
-                  }
-               }
-            }
-
-            /* check to see if any watchdogs are due for this
-               notification.
-
-               bits        is the set of notification bits
-               timeoutBits is the set of timed out bits
-            */
-
-            bits = gpioNotify[n].bits;
-
-            if (timeoutBits & bits)
-            {
-               /* at least one watchdog has fired for this
-                  notification.
-               */
-
-               for (b=0; b<=PI_MAX_USER_GPIO; b++)
-               {
-                  if (timeoutBits & bits & (1<<b))
-                  {
-                     if (numSamples)
-                        newLevel = sample[numSamples-1].level;
-                     else
-                        newLevel = reportedLevel;
-
-                     gpioReport[emit].seqno = seqno;
-                     gpioReport[emit].flags = PI_NTFY_FLAGS_WDOG |
-                                              PI_NTFY_FLAGS_BIT(b);
-                     gpioReport[emit].tick  = stick;
-                     gpioReport[emit].level = newLevel;
-
-                     emit++;
-                     seqno++;
-                  }
-               }
-            }
-
-            if (!emit)
-            {
-               if ((stick - gpioNotify[n].lastReportTick) > 60000000)
-               {
-                  if (numSamples)
-                     newLevel = sample[numSamples-1].level;
-                  else
-                     newLevel = reportedLevel;
-
-                  gpioReport[emit].seqno = seqno;
-                  gpioReport[emit].flags = PI_NTFY_FLAGS_ALIVE;
-                  gpioReport[emit].tick  = stick;
-                  gpioReport[emit].level = newLevel;
-
-                  emit++;
-                  seqno++;
-               }
-            }
-
-            if (emit)
-            {
-               DBG(DBG_FAST_TICK, "notification %d (%d reports, %x-%x)",
-                  n, emit, gpioReport[0].seqno,  gpioReport[emit-1].seqno);
-               gpioNotify[n].lastReportTick = stick;
-               max_emits = gpioNotify[n].max_emits;
-
-               if (emit > gpioStats.maxEmit) gpioStats.maxEmit = emit;
-
-               emitted = 0;
-
-               while (emit > 0)
-               {
-                  if (emit > max_emits)
-                  {
-                     gpioStats.emitFrags++;
-
-                     err = write(gpioNotify[n].fd,
-                              gpioReport+emitted,
-                              max_emits*sizeof(gpioReport_t));
-
-                     if (err != (max_emits*sizeof(gpioReport_t)))
-                     {
-                        if (err < 0)
-                        {
-                           if ((errno != EAGAIN) && (errno != EWOULDBLOCK))
-                           {
-                              /* serious error, no point continuing */
-
-                              DBG(DBG_ALWAYS, "fd=%d err=%d errno=%d",
-                                 gpioNotify[n].fd, err, errno);
-
-                              DBG(DBG_ALWAYS, "%s", strerror(errno));
-
-                              gpioNotify[n].bits  = 0;
-                              gpioNotify[n].state = PI_NOTIFY_CLOSING;
-                              triggerAlert2 = 1;
-                              intNotifyBits();
-                              break;
-                           }
-                           else gpioStats.wouldBlockPipeWrite++;
-                        }
-                        else
-                        {
-                           gpioCfg.internals |= PI_CFG_STATS;
-                           gpioStats.shortPipeWrite++;
-                           DBG(DBG_ALWAYS, "emitted %d, asked for %d",
-                              err/sizeof(gpioReport_t), max_emits);
-                        }
-                     }
-                     else gpioStats.goodPipeWrite++;
-
-                     emitted += max_emits;
-                     emit    -= max_emits;
-                  }
-                  else
-                  {
-                     err = write(gpioNotify[n].fd,
-                              gpioReport+emitted,
-                              emit*sizeof(gpioReport_t));
-
-                     if (err != (emit*sizeof(gpioReport_t)))
-                     {
-                        if (err < 0)
-                        {
-                           if ((errno != EAGAIN) && (errno != EWOULDBLOCK))
-                           {
-                              DBG(DBG_ALWAYS, "fd=%d err=%d errno=%d",
-                                 gpioNotify[n].fd, err, errno);
-
-                              DBG(DBG_ALWAYS, "%s", strerror(errno));
-
-                              /* serious error, no point continuing */
-                              gpioNotify[n].bits  = 0;
-                              gpioNotify[n].state = PI_NOTIFY_CLOSING;
-                              triggerAlert2 = 1;
-                              intNotifyBits();
-                              break;
-                           }
-                           else gpioStats.wouldBlockPipeWrite++;
-                        }
-                        else
-                        {
-                           gpioCfg.internals |= PI_CFG_STATS;
-                           gpioStats.shortPipeWrite++;
-                           DBG(DBG_ALWAYS, "emitted %d, asked for %d",
-                              err/sizeof(gpioReport_t), emit);
-                        }
-                     }
-                     else gpioStats.goodPipeWrite++;
-
-                     emitted += emit;
-                     emit = 0;
-                  }
-               }
-
-               gpioNotify[n].seqno = seqno;
-            }
-         }
-      }
-
-      if (changedBits & scriptBits)
-      {
-         for (n=0; n<PI_MAX_SCRIPTS; 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);
-            }
+            req.tv_sec  = rem.tv_sec;
+            req.tv_nsec = rem.tv_nsec;
          }
       }
-
-      if (numSamples) reportedLevel = sample[numSamples-1].level;
    }
 
    return 0;
@@ -7237,10 +7173,7 @@ static void initClearGlobals(void)
    nFilterBits = 0;
    wdogBits    = 0;
 
-   triggerAlert2 = 0;
-
-   pthAlert1Running  = 0;
-   pthAlert2Running  = 0;
+   pthAlertRunning  = 0;
    pthFifoRunning   = 0;
    pthSocketRunning = 0;
 
@@ -7318,8 +7251,6 @@ static void initClearGlobals(void)
    fdLock       = -1;
    fdMem        = -1;
    fdSock       = -1;
-   fdAlertRead  = -1;
-   fdAlertWrite = -1;
 
    dmaMboxBlk = MAP_FAILED;
    dmaPMapBlk = MAP_FAILED;
@@ -7368,18 +7299,11 @@ static void initReleaseResources(void)
       }
    }
 
-   if (pthAlert1Running)
-   {
-      pthread_cancel(pthAlert1);
-      pthread_join(pthAlert1, NULL);
-      pthAlert1Running = 0;
-   }
-
-   if (pthAlert2Running)
+   if (pthAlertRunning)
    {
-      pthread_cancel(pthAlert2);
-      pthread_join(pthAlert2, NULL);
-      pthAlert2Running = 0;
+      pthread_cancel(pthAlert);
+      pthread_join(pthAlert, NULL);
+      pthAlertRunning = 0;
    }
 
    if (pthFifoRunning)
@@ -7510,18 +7434,6 @@ static void initReleaseResources(void)
       fdMbox = -1;
    }
 
-   if (fdAlertRead != -1)
-   {
-      close(fdAlertRead);
-      fdAlertRead = -1;
-   }
-
-   if (fdAlertWrite != -1)
-   {
-      close(fdAlertWrite);
-      fdAlertWrite = -1;
-   }
-
    gpioStats.DMARestarts = 0;
    gpioStats.dmaInitCbsCount = 0;
 }
@@ -7594,15 +7506,10 @@ int initInitialise(void)
    if (pthread_attr_setstacksize(&pthAttr, STACK_SIZE))
       SOFT_ERROR(PI_INIT_FAILED, "pthread_attr_setstacksize failed (%m)");
 
-   if (pthread_create(&pthAlert1, &pthAttr, pthAlert1Thread, &i))
-      SOFT_ERROR(PI_INIT_FAILED, "pthread_create alert failed (%m)");
+   if (pthread_create(&pthAlert, &pthAttr, pthAlertThread, &i))
+      SOFT_ERROR(PI_INIT_FAILED, "pthread_create alert failed (%m)");
 
-   pthAlert1Running = 1;
-
-   if (pthread_create(&pthAlert2, &pthAttr, pthAlert2Thread, &i))
-      SOFT_ERROR(PI_INIT_FAILED, "pthread_create alert 2 failed (%m)");
-
-   pthAlert2Running = 1;
+   pthAlertRunning = 1;
 
    if (!(gpioCfg.ifFlags & PI_DISABLE_FIFO_IF))
    {
@@ -10518,11 +10425,9 @@ int gpioNotifyClose(unsigned handle)
 
    gpioNotify[handle].state = PI_NOTIFY_CLOSING;
 
-   triggerAlert2 = 1;
-
    intNotifyBits();
 
-   /* actual close done in alert2 thread */
+   /* actual close done in alert thread */
 
    return 0;
 }
index dc9c40fb9a50ef0f26a4beb4d8ab68151c8a80c6..3c8d329610a560721a49f9e9ab5afeced456a63b 100644 (file)
--- a/pigpio.h
+++ b/pigpio.h
@@ -31,7 +31,7 @@ For more information, please refer to <http://unlicense.org/>
 #include <stdint.h>
 #include <pthread.h>
 
-#define PIGPIO_VERSION 44
+#define PIGPIO_VERSION 45
 
 /*TEXT
 
index 45549504d4523dd461178bdb4cdd7d0a9f18d38b..50212b01ece1bccdab10270a1ff1e0fe901a7ecf 100644 (file)
@@ -522,12 +522,12 @@ void t7()
    /* type of edge shouldn't matter for watchdogs */
    gpioSetAlertFunc(GPIO, t7cbf);
 
-   gpioSetWatchdog(GPIO, 10); /* 10 ms, 100 per second */
+   gpioSetWatchdog(GPIO, 50); /* 50 ms, 20 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");
+   CHECK(7, 1, c, 39, 5, "set watchdog on count");
 
    gpioSetWatchdog(GPIO, 0); /* 0 switches watchdog off */
    time_sleep(0.5);
index 261a71acbb621e6f5aa94671a8dd9c20340d1f64..9136f48de2050d845ec3363e3bd5700453b2e9bc 100755 (executable)
@@ -109,7 +109,6 @@ def t2():
    f = t2_count - oc
    CHECK(2, 3, f, 0, 0, "set PWM dutycycle, callback")
 
-
    pi.set_PWM_dutycycle(GPIO, 128)
    dc = pi.get_PWM_dutycycle(GPIO)
    CHECK(2, 4, dc, 128, 0, "get PWM dutycycle")
@@ -155,6 +154,8 @@ def t2():
 
    pi.set_PWM_dutycycle(GPIO, 0)
 
+   t2cb.cancel()
+
 t3_reset=True
 t3_count=0
 t3_tick=0
@@ -234,6 +235,8 @@ def t3():
 
    pi.set_PWM_dutycycle(GPIO, 0)
 
+   t3cb.cancel()
+
 def t4():
 
    print("Pipe notification tests.")
@@ -465,6 +468,8 @@ To the lascivious pleasing of a lute.
    e = pi.wave_delete(0)
    CHECK(5, 33, e, 0, 0, "wave delete")
 
+   t5cb.cancel()
+
 t6_count=0
 t6_on=0
 t6_on_tick=None
@@ -501,6 +506,8 @@ def t6():
 
    CHECK(6, 2, t6_on, tp, 25, "gpio trigger pulse length")
 
+   t6cb.cancel()
+
 t7_count=0
 
 def t7cbf(gpio, level, tick):
@@ -516,12 +523,12 @@ def t7():
    # type of edge shouldn't matter for watchdogs
    t7cb = pi.callback(GPIO, pigpio.FALLING_EDGE, t7cbf)
 
-   pi.set_watchdog(GPIO, 10) # 10 ms, 100 per second
+   pi.set_watchdog(GPIO, 50) # 50 ms, 20 per second
    time.sleep(0.5)
    oc = t7_count
    time.sleep(2)
    c = t7_count - oc
-   CHECK(7, 1, c, 200, 5, "set watchdog on count")
+   CHECK(7, 1, c, 39, 5, "set watchdog on count")
 
    pi.set_watchdog(GPIO, 0) # 0 switches watchdog off
    time.sleep(0.5)
@@ -530,6 +537,8 @@ def t7():
    c = t7_count - oc
    CHECK(7, 2, c, 0, 1, "set watchdog off count")
 
+   t7cb.cancel()
+
 def t8():
    print("Bank read/write tests.")
 
@@ -661,6 +670,8 @@ def t9():
    e = pi.delete_script(s)
    CHECK(9, 4, e, 0, 0, "delete script")
 
+   t9cb.cancel()
+
    pigpio.exceptions = old_exceptions
 
 def ta():
@@ -830,6 +841,116 @@ def tc():
    e = pi.spi_close(h)
    CHECK(12, 99, e, 0, 0, "spi close")
 
+def td():
+
+   print("Wavechains & filter tests.")
+
+   tdcb = pi.callback(GPIO)
+
+   pi.set_mode(GPIO, pigpio.OUTPUT)
+
+   pi.write(GPIO, pigpio.LOW)
+
+   e = pi.wave_clear()
+   CHECK(13, 1, e, 0, 0, "callback, set mode, wave clear")
+
+   wf = []
+
+   wf.append(pigpio.pulse(1<<GPIO, 0,  50))
+   wf.append(pigpio.pulse(0, 1<<GPIO,  70))
+   wf.append(pigpio.pulse(1<<GPIO, 0, 130))
+   wf.append(pigpio.pulse(0, 1<<GPIO, 150))
+   wf.append(pigpio.pulse(1<<GPIO, 0,  90))
+   wf.append(pigpio.pulse(0, 1<<GPIO, 110))
+
+   e = pi.wave_add_generic(wf)
+   CHECK(13, 2, e, 6, 0, "pulse, wave add generic")
+
+   wid = pi.wave_create()
+
+   chain = [
+      255, 0, wid, 255, 1, 128, 0, 255, 2, 0, 8,
+      255, 0, wid, 255, 1,   0, 1, 255, 2, 0, 4,
+      255, 0, wid, 255, 1,   0, 2]
+
+   e = pi.set_glitch_filter(GPIO, 0)
+   CHECK(13, 3, e, 0, 0, "clear glitch filter")
+
+   e = pi.set_noise_filter(GPIO, 0, 0)
+   CHECK(13, 4, e, 0, 0, "clear noise filter")
+
+   tdcb.reset_tally()
+   e = pi.wave_chain(chain)
+   CHECK(13, 5, e, 0, 0, "wave chain")
+   while pi.wave_tx_busy():
+      time.sleep(0.1)
+   time.sleep(0.3)
+   tally = tdcb.tally()
+   CHECK(13, 6, tally, 2688, 2, "wave chain, tally")
+
+   pi.set_glitch_filter(GPIO, 80)
+   tdcb.reset_tally()
+   pi.wave_chain(chain)
+   while pi.wave_tx_busy():
+      time.sleep(0.1)
+   time.sleep(0.3)
+   tally = tdcb.tally()
+   CHECK(13, 7, tally, 1792, 2, "glitch filter, wave chain, tally")
+
+   pi.set_glitch_filter(GPIO, 120)
+   tdcb.reset_tally()
+   pi.wave_chain(chain)
+   while pi.wave_tx_busy():
+      time.sleep(0.1)
+   time.sleep(0.2)
+   tally = tdcb.tally()
+   CHECK(13, 8, tally, 896, 2, "glitch filter, wave chain, tally")
+
+   pi.set_glitch_filter(GPIO, 140)
+   tdcb.reset_tally()
+   pi.wave_chain(chain)
+   while pi.wave_tx_busy():
+      time.sleep(0.1)
+   time.sleep(0.2)
+   tally = tdcb.tally()
+   CHECK(13, 9, tally, 0, 0, "glitch filter, wave chain, tally")
+
+   pi.set_glitch_filter(GPIO, 0)
+
+   pi.wave_chain(chain)
+   pi.set_noise_filter(GPIO, 1000, 150000)
+   tdcb.reset_tally()
+   while pi.wave_tx_busy():
+      time.sleep(0.1)
+   time.sleep(0.2)
+   tally = tdcb.tally()
+   CHECK(13, 10, tally, 1500, 2, "noise filter, wave chain, tally")
+
+   pi.wave_chain(chain)
+   pi.set_noise_filter(GPIO, 2000, 150000)
+   tdcb.reset_tally()
+   while pi.wave_tx_busy():
+      time.sleep(0.1)
+   time.sleep(0.2)
+   tally = tdcb.tally()
+   CHECK(13, 11, tally, 750, 2, "noise filter, wave chain, tally")
+
+   pi.wave_chain(chain)
+   pi.set_noise_filter(GPIO, 3000, 5000)
+   tdcb.reset_tally()
+   while pi.wave_tx_busy():
+      time.sleep(0.1)
+   time.sleep(0.2)
+   tally = tdcb.tally()
+   CHECK(13, 12, tally, 0, 2, "noise filter, wave chain, tally")
+
+   pi.set_noise_filter(GPIO, 0, 0)
+
+   e = pi.wave_delete(wid)
+   CHECK(13, 13, e, 0, 0, "wave delete")
+
+   tdcb.cancel()
+
 if len(sys.argv) > 1:
    tests = ""
    for C in sys.argv[1]:
@@ -838,7 +959,7 @@ if len(sys.argv) > 1:
          tests += c
 
 else:
-   tests = "0123456789"
+   tests = "0123456789d"
 
 pi = pigpio.pi()
 
@@ -858,6 +979,7 @@ if pi.connected:
    if 'a' in tests: ta()
    if 'b' in tests: tb()
    if 'c' in tests: tc()
+   if 'd' in tests: td()
 
 pi.stop()
 
index 17fd8a871bc4d432f4d8496e959246b215cfd5ef..df2af4b3db8acce9bd41f2a0b51690ef5d8228d0 100644 (file)
@@ -490,12 +490,12 @@ void t7()
    /* type of edge shouldn't matter for watchdogs */
    callback(GPIO, FALLING_EDGE, t7cbf);
 
-   set_watchdog(GPIO, 10); /* 10 ms, 100 per second */
+   set_watchdog(GPIO, 50); /* 50 ms, 20 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");
+   CHECK(7, 1, c, 39, 5, "set watchdog on count");
 
    set_watchdog(GPIO, 0); /* 0 switches watchdog off */
    time_sleep(0.5);
index ec2da1e7f4002f51f940dd2c442d12063d985c23..5e184697472399b3e4114bd86cb8297ec3cc8840 100644 (file)
@@ -498,12 +498,12 @@ void t7(int pi)
    /* type of edge shouldn't matter for watchdogs */
    id = callback(pi, GPIO, FALLING_EDGE, t7cbf);
 
-   set_watchdog(pi, GPIO, 10); /* 10 ms, 100 per second */
+   set_watchdog(pi, GPIO, 50); /* 50 ms, 20 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");
+   CHECK(7, 1, c, 39, 5, "set watchdog on count");
 
    set_watchdog(pi, GPIO, 0); /* 0 switches watchdog off */
    time_sleep(0.5);