Luka Vlaskalic
Luka Vlaskalic

Reputation: 465

How to loop a function to perform a task every 15 minutes (on the 0/15/30/45 minute mark)?

I have a program running on a Raspberry Pi, and I want to pull some data from a thermometer every 15 minutes at 0, 15, 30 and 45 minutes past the hour.

I have tried this using a while loop, I previously used time.sleep(900) effectively, but this sometimes drifted away from 0, 15, 30 and 45 minutes past the hour.

At the moment I currently have this;

from datetime import datetime

def run(condition):
    while condition == True:
        if (datetime.now().minute == (0 or 15 or 30 or 45)):
            #perform some task
        temperature_store()

For sake of simplicity I have not got into what temperature_store() does, but it reads the temperature from a sensor plugged into the Pi and then prints it.

I want temperature_store() to occur every 15 minutes, but currently, it is happening every second.

I am aware that it is probably because I have the logic/syntax of the while loop wrong, but I cannot figure it out. (Do not have much experience with python scripts and delays in time).

Upvotes: 6

Views: 15070

Answers (3)

Geza Kerecsenyi
Geza Kerecsenyi

Reputation: 1218

There's two ways to do this: the 'easy' way, and the stable way.

The easy way is simply to do the following:

from datetime import datetime
from time import sleep

def run(condition):
    while datetime.now().minute not in {0, 15, 30, 45}:  # Wait 1 second until we are synced up with the 'every 15 minutes' clock
        sleep(1)

    def task():
        # Your task goes here
        # Functionised because we need to call it twice
        temperature_store()
    
    task()

    while condition == True:
        sleep(60*15)  # Wait for 15 minutes
        task()

This essentially waits until we are synced up with the correct minute, then executes it, and simply waits 15 minutes before looping. Use it if you wish, it's the simplest way in pure Python. The issues with this are countless, however:

  • It syncs up with the minute, not the second
  • It's machine dependent, and may give incorrect readings on some machines
  • It needs to run continuously!

The second method is to use cron-jobs, as suggested in the comments. This is superior because:

  • It uses system-level events, not timers, so we can assure the best possible accuracy
  • Since we're not waiting, there's no room for error
  • It only runs the function once it gets the aforementioned event setter.

So, to use, simply (assuming you're on Linux):

from crontab import CronTab

cron = CronTab(user='username')  # Initialise a new CronTab instance
job = cron.new(command='python yourfile.py')  # create a new task
job.minute.on(0, 15, 30, 45)  # Define that it should be on every 0th, 15th, 30th and 45th minute

cron.write()  # 'Start' the task (i.e trigger the cron-job, but through the Python library instead

(obviously, configure username appropriately)

In yourfile.py, in the same path, simply put your code for temperature_store().

I hope this helps. Obviously, go with the first methods or even the suggestions in the comments if you prefer, but I just felt that the entire loop structure was a bit too fragile, especially on a Raspberry Pi. This should hopefully be something more stable and scalable, should you want to hook up other IoT things to your Pi.

Upvotes: 8

Luka Vlaskalic
Luka Vlaskalic

Reputation: 465

I have come up with the following answer, using some of the logic from @geza-kerecsenyi 's answer

def run():
    first = True
    while first == True:
        second = True
        while second == True:
            if datetime.now().minute == 0 or datetime.now().minute ==15 or datetime.now().minute ==30 or datetime.now().minute == 45:
                action()
                sleep(899)

I am not sure which of these is better in terms of CPU or which is more effective to run, but the logic seems sound on both.

[@geza-kerecsenyi will mark your answer as correct]

Upvotes: 0

Sihyeon Kim
Sihyeon Kim

Reputation: 161

I did the module test in case of the program that prints seconds every 15 seconds.
The below code does not contain sleep().
You can change second to minute in below code to do tasks every 15 minutes.

from datetime import datetime

while True:
    a = datetime.now().second
    if (a % 15) == 0:  # every 15 seconds
        print(a)  # ---- To do something every 15 seconds ---- #
        while True:  # discard any milliseconds or duplicated 15 seconds
            a = datetime.now().second
            if (a % 15) is not 0:
                break

But I think that cron or any other schduler modules are good choices.

Upvotes: 0

Related Questions