For an index to all my stories click this text.
What's this story about.
This story shows how to get the accurate time from an NTP server and use that to perform a daily task, every day at the same time. The program is written in MicroPython and will work on a Raspberry Pi Pico W as well as on an ESP32 or ESP8266.
A daily task ??
You can use this to set the coffee machine on every day at the same time so you'll have a fresh cup of java every morning when you wake.
Another option is to water your plants every day at the same time when you are on a holiday. Or use this to build an automatic fish feeder that feeds the fish once a day. You could even make an alarm clock that wakes you every day. Plenty of tasks you can use this for. Just use your imagination.
Actually I wrote this story because I got a question on my Discord server from one of my readers, who wanted to know if I found a way to execute a daily task by using the NTP server. As you may have noticed I am no longer on Discord. if you want to reqach me, please do so by mail.
CHAT_GPT
My Discord server had a CHAT-GPT section where you could ask questions to CHAT-GPT. So I asked CHAT-GPT to write a MicroPython program that achieved this. And CHAT-GPT came up with this:
Besides the fact that this code is not going to retrieve the NTP time in any way, there are some issues with it.
- The rp2040 has a RTC in which you can set hours, minutes, seconds, day, month, and year, then read back the current time later. Unfortunately the Pico board clock oscillator is rated at about 30 ppm. Since there are 86400 seconds/day, this means a deviation of up to 2.6 seconds a day. That does not look much however is about 16 minutes a year.
- I showed in a previous story that the NTP time actually is the UTC time which does not take in account your timezone or Dailight Savings Time (DST).
Corrections
To get this working we need to make several corrections:
- Get the Actual UTC time from an NTP server
- Adjust the time for your timezone
- Adjust that time for DST (Dailight Savings time)
- Do the above every day to correct the internal clock
After these steps we can check for a certain time (hour and minutes) for the task to start.
Timezone and DST
First thing I did is creating a function that uses a timezone as a parameter. That function looks like this:
def settime(timezone): global local_time rtc = machine.RTC() # Set the time from NTP server ntptime.settime() time.sleep(2) # Get the local time local_time = time.localtime() #adjust for timezone Netherlands local_time = time.localtime(time.mktime(local_time) + (timezone*3600)) # Adjust for DST if necessary if is_dst_europe(local_time): local_time = time.localtime(time.mktime(local_time) + 3600) return
This function depends on another function to adjust the retrieved time for the European DST (Dailight saving time).
# Function to check if DST is in effect def is_dst_europe(t): year, month, day, hour, minute, second, weekday, yearday = t print(t) # Last Sunday in March #dst_start = max(week for week in range(25, 32) if time.localtime(time.mktime((year, 3, week, 1, 0, 0, 0, 0, 0)))[6] == 6) dst_start = 0 for day in range(25, 32): if time.localtime(time.mktime((year, month, day, 1, 0, 0, 0, 0, 0)))[6] == 6: dst_start = day # Last Sunday in October #dst_end = max(week for week in range(25, 32) if time.localtime(time.mktime((year, 10, week, 1, 0, 0, 0, 0, 0)))[6] == 6) dst_end = 0 for day in range(25, 32): if time.localtime(time.mktime((year, month, day, 1, 0, 0, 0, 0, 0)))[6] == 6: dst_end = day start = time.mktime((year, 3, dst_start, 1, 0, 0, 0, 0, 0)) end = time.mktime((year, 10, dst_end, 1, 0, 0, 0, 0, 0)) now = time.mktime(t) return start <= now < end
To call these functions use this line:
settime(1)
Adjust the 1 for your own timezone.
Test if a new day has started
Like described in the beginning of this story the Pico's internal clock has a deviation of about 3 seconds a day. To correct that we will have to the correct time once a day. Here is the code that checks if a new day has started.
# Function to get the current date def get_current_date(): current_time = time.localtime() return current_time[0], current_time[1], current_time[2] # year, month, day # Initialize the previous date previous_date = get_current_date() while True: current_date = get_current_date() # Check if the day has changed if current_date != previous_date: print("A new day has started!") previous_date = current_date
What this code does is checking if the year, month and day of the previous_day variable are equal to the current_date varaiable. If that is not the case then a new day has begun and the previous_date is set to the current_date so the cycle starts anew.
Start a task every day at the same time
Now that we have gotten the exact time we can use that to start our task every day as the same time.
import time # Function to perform the daily task def daily_task(): print("This task runs every day at 10:00 AM") # Set the target time for the task target_hour = 10 target_minute = 0 while True: current_time = time.localtime() current_hour = current_time[3] current_minute = current_time[4] # Check if the current time matches the target time if current_hour == target_hour and current_minute == target_minute: daily_task() # Wait for a minute to avoid running the task multiple times within the same minute time.sleep(60) # Sleep for a short while before checking the time again time.sleep(1)
This part is easy. We check the current hour which is the 4th entry in the current_time tuple (number 3). Then we check the current minutes which is the 5th entry (number 4). Then these are compared to the hour and time we defined as the starting time. If these are the same the task is started.
The complete program.
All in all this is a lot of code to get a task starting every day at the same time. Below is the complete program in which all the above functions are combined together with the code for accessing the internet.
The NTP server library can be obtained from my previous story which you can find here: https://lucstechblog.blogspot.com/2023/04/getting-right-time-with-micropython.html
import network import ntptime import time import machine # Set your router credentials ssid = "YOUR-ROUTERS-NAME" pw = "PASSWORD" # At what hour and minutes should the task start target_hour = 22 target_minute = 47 # set the local time to the internal time # to initialise the local_time variable local_time = machine.RTC() # Set the timezone timezoneadjust = 1 # Start the wifi connection wifi = network.WLAN(network.STA_IF) wifi.active(True) wifi.connect(ssid, pw) # wait for connection print('Waiting for connection.',end="") while wifi.isconnected() == False: time.sleep(1) print('', end='.') print("") ip = wifi.ifconfig()[0] print("Connected with IP adress : "+ip) time.sleep(1) # set the time from the server ntptime.settime() # set date for date checking current_time = time.localtime() previous_date = current_time[0], current_time[1], current_time[2] # =============================================================== # Function to check if DST is in effect def is_dst_europe(t): year, month, day, hour, minute, second, weekday, yearday = t # print(t) # Test last Sunday in March dst_start = 0 for day in range(25, 32): if time.localtime(time.mktime((year, month, day, 1, 0, 0, 0, 0, 0)))[6] == 6: dst_start = day # Last Sunday in October dst_end = 0 for day in range(25, 32): if time.localtime(time.mktime((year, month, day, 1, 0, 0, 0, 0, 0)))[6] == 6: dst_end = day start = time.mktime((year, 3, dst_start, 1, 0, 0, 0, 0, 0)) end = time.mktime((year, 10, dst_end, 1, 0, 0, 0, 0, 0)) now = time.mktime(t) return start <= now < end #=========================================================== def settime(timezoneadjust): # Get the local time local_time = time.localtime() #adjust for timezone Netherlands local_time = time.localtime(time.mktime(local_time) + (timezoneadjust*3600)) # Adjust for DST if necessary if is_dst_europe(local_time): local_time = time.localtime(time.mktime(local_time) + 3600) return (local_time) #=============================================================== def testday(): # Function to get the current date global previous_date current_time = settime(timezoneadjust) current_date = current_time[0], current_time[1], current_time[2] # year, month, day # Check if the day has changed if current_date != previous_date: print("A new day has started!") previous_date = current_date ntptime.settime() else: print("Still the same date") return #================================================= # Start of the actual program while True: current_time = settime(timezoneadjust) print("Adjusted time:", current_time) testday() # print("in the while",current_time) current_hour = current_time[3] print(current_hour) current_minute = current_time[4] print(current_minute) # Check if the current time matches the target time # Then here comes the daily task if current_hour == target_hour and current_minute == target_minute: print ("Here comes the daily task") # Wait for a minute to avoid running the task multiple times within the same minute time.sleep(60) time.sleep(10)
You can copy this code and paste it in Thonny's editor to transfer it to your microcontroller,
In this example the time for the daily task is set at 22:47. And you need to change the code at where it says: Here comes the daily task to fill in the task you want to have performed.
Expansion
By adding multiple target hours and minutes and use multiple tests in the main part of the program you can use this for scheduling multiple tasks in one day.
Till next time
Have fun
Luc Volders