Python: Run code every n seconds and restart timer on condition

10,176

Solution 1

If the whole process is as simple as you say, I would go about it like this (semi-psuedo-code):

def run_every_fifteen_minutes():
  pass
def should_reset_timer():
  pass
def main():
  timer = 0
  while True:
    time.sleep(1)
    timer+=1
    if should_reset_timer():
      timer = 0
    if timer == 15*60:
      run_every_fifteen_minutes()
      timer = 0

Note that this won't be exactly fifteen minutes in. It might be late by a few seconds. The sleep isn't guaranteed to sleep only 1 second and the rest of the loop will take some time, too. You could add a system time compare in there if you need it to be really accurate.

Solution 2

Thanks for the help everyone, your answers pointed me in the right direction. In the end I came up with:

#!/usr/bin/python
import RPi.GPIO as GPIO
import time
import subprocess

GPIO.setmode(GPIO.BCM)
PIR_PIN = 4
GPIO.setup(PIR_PIN, GPIO.IN)
timer = 15 * 60 # 60 seconds times 15 mins

subprocess.call("sudo /opt/vc/bin/tvservice -o", shell=True)

try :
    print "Screen Timer (CTRL+C to exit)"
    time.sleep(5)
    print "Ready..."

    while True:
        time.sleep(0.985)

        # Test PIR_PIN condition
        current_state = GPIO.input(PIR_PIN)

        if timer > 0:
            timer -= 1

            if current_state: #is true
                # Reset timer
                timer = 15 * 60

        else:
            if current_state: #is true
                subprocess.call("sudo /opt/vc/bin/tvservice -p", shell=True)

                # Reset timer
                timer = 15 * 60
            else:
                subprocess.call("sudo /opt/vc/bin/tvservice -o", shell=True)

except KeyboardInterrupt:
    print "Quit"
    GPIO.cleanup()

To put it in context, I'm using a PIR sensor to detect motion and switch on an hdmi connected monitor on a Raspberry Pi. After 15 mins of no movement I want to switch the monitor off and then if (at a later time) movement is detected, switch it back on again and restart the time.

Solution 3

The description sounds similar to a dead main's switch / watchdog timer. How it is implemented depends on your application: whether there is an event loop, are there blocking functions, do you need a separate process for proper isolation, etc. If no function is blocking in your code:

#!/usr/bin/env python3
import time
from time import time as timer

timeout = 900 # 15 minutes in seconds
countdown = timeout # reset the count
while True:
    time.sleep(1 - timer() % 1) # lock with the timer, to avoid drift
    countdown -= 1
    if should_reset_count():
        countdown = timeout # reset the count
    if countdown <= 0: # timeout happened
        countdown = timeout # reset the count
        "some code is executed"

The code assumes that the sleep is never interrupted (note: before Python 3.5, the sleep may be interrupted by a signal). The code also assumes no function takes significant (around a second) time. Otherwise, you should use an explicit deadline instead (the same code structure):

deadline = timer() + timeout # reset
while True:
    time.sleep(1 - timer() % 1) # lock with the timer, to avoid drift
    if should_reset_count():
        deadline = timer() + timeout # reset
    if deadline < timer(): # timeout
        deadline = timer() + timeout # reset
        "some code is executed"
Share:
10,176
Chris J
Author by

Chris J

I work at boulder-design.co.uk helping charities, NGOs and third sector groups make their mark on the web. Having got the gist of PHP, I'm discovering the delights of MODX and Laravel as well as trying to branch out into other languages.

Updated on June 05, 2022

Comments

  • Chris J
    Chris J almost 2 years

    This may be simpler than I think but I'd like to create timer that, upon reaching a limit (say 15 mins), some code is executed.

    Meanwhile every second, I'd like to test for a condition. If the condition is met, then the timer is reset and the process begins again, otherwise the countdown continues.

    If the condition is met after the countdown has reached the end, some code is executed and the timer starts counting down again.

    Does this involve threading or can it be achieved with a simple time.sleep() function?