ESP32/ESP8266: Run Daily Task at Specific Time (Arduino IDE)

In this guide, we’ll show you how to get date and time using the ESP32 or ESP8266 to run one or multiple tasks every day at an exact time. For this application, your ESP boards will get the time using Network Time Protocol (NTP), so they must be connected to the Internet. The ESP boards will be programmed using Arduino IDE.

ESP32 ESP8266 NodeMCU Run Daily Task at Specific Time Arduino IDE

Prerequisites

Before proceeding make sure you have the ESP32 or ESP8266 boards add-on installed in Arduino IDE:

For this tutorial, you only need an ESP32 or an ESP8266 board:

NTP (Network Time Protocol)

To get track of time, we’ll be using NTP. NTP stands for Network Time Protocol and it is a networking protocol for clock synchronization between computer systems. In other words, it is used to synchronize computer clock times in a network.

There are NTP servers like pool.ntp.org that anyone can use to request time as a client. In this case, the ESP32/ESP8266 is an NTP Client that requests time from an NTP Server (pool.ntp.org).

NTP Network Time Protocol ESP32 Request time and date

If you want to learn more about NTP Client-Server interaction with the ESP boards, you can read the following guides:

For this guide you’ll be using the default time.h library that comes with the Arduino framework so you don’t need to install additional libraries.

ESP32/ESP8266 Run Daily Task at Specific Time – Code

To get date and time with the ESP boards, you don’t need to install any libraries. You simply include the time.h library in your code.

The following code gets date and time from the NTP Server, prints the results on the Serial Monitor, and checks if the daily task has run or not.

/*  
  Rui Santos & Sara Santos - Random Nerd Tutorials
  https://RandomNerdTutorials.com/esp32-esp8266-run-daily-task/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <Arduino.h>
#if defined(ESP32)
  #include <WiFi.h>
#elif defined(ESP8266)
  #include <ESP8266WiFi.h>
#endif
#include <time.h>

// Replace with your network credentials
const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Timezone string for your region, example: https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
const char* timezone = "WET0WEST,M3.5.0/1,M10.5.0"; // WET0WEST,M3.5.0/1,M10.5.0 = Europe/Lisbon

// Time that the daily task runs in 24 hour format
const int taskHour = 16;   // Hour example in 24 hour format: 16 = 4 PM
const int taskMinute = 5;  // 5 minutes

// Store the day when the task last ran to ensure it only runs once per day
int lastRunDay = -1;

unsigned long lastNTPUpdate = 0; // Timestamp for the last NTP sync
const unsigned long ntpSyncInterval = 30 * 60 * 1000; // Sync every 30 minutes (in ms)

void syncTime() {
  Serial.print("Synchronizing time with NTP server...");
  configTime(0, 0, "pool.ntp.org", "time.nist.gov"); // UTC offset set to 0
  time_t now = time(nullptr);
  while (now < 24 * 3600) { // Wait until time is valid
    delay(100);
    now = time(nullptr);
  }
  Serial.println(" Time synchronized!");
  
  // Set timezone
  setenv("TZ", timezone, 1);
  tzset();

  lastNTPUpdate = millis(); // Record the time of the last sync
}

void setup() {
  Serial.begin(115200);
  
  // Connect to Wi-Fi
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected.");

  syncTime();
}

void loop() {
  time_t now = time(nullptr);
  struct tm timeinfo;
  localtime_r(&now, &timeinfo);

  // Current time and date
  Serial.printf("Current time: %02d:%02d:%02d, Date: %04d-%02d-%02d\n",
              timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec,
              timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday);

  // Check if it's time to run the daily task
  if (timeinfo.tm_hour == taskHour && timeinfo.tm_min == taskMinute && lastRunDay != timeinfo.tm_mday) {
    dailyTask();
    // Set the day to ensure it only runs once per day
    lastRunDay = timeinfo.tm_mday;
  }

  // Resynchronize with NTP every 30 minutes
  if (millis() - lastNTPUpdate > ntpSyncInterval) {
    syncTime();
  }

  delay(1000); // Run loop every second
}

void dailyTask() {
  Serial.println("#########\nDoing daily task...\n#########");
  // ENTER YOUR TASK HERE
}

View raw code

How the Code Works

Let’s take a quick look at the code to see how it works. First, include the libraries to connect to Wi-Fi and get time.

#include <Arduino.h>
#if defined(ESP32)
  #include <WiFi.h>
#elif defined(ESP8266)
  #include <ESP8266WiFi.h>
#endif
#include <time.h>

Setting SSID and Password

Type your network credentials in the following variables, so that the ESP32/ESP8266 is able to establish an Internet connection and get date and time from the NTP server.

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Set timezone Variable

Then, you need to define the timezone variable to retrieve the correct time for your location.

const char* timezone = "WET0WEST,M3.5.0/1,M10.5.0";

For example, I live in Porto. The time zone is Europe/Lisbon. From the list of time zone string variables, I see that the time zone string variable for my location is WET0WEST,M3.5.0/1,M10.5.0. You can check a list of time zone string variables here.

Set the Hour and Minute the Task Runs

Set the hour and minute you want your daily task to run. The time for the daily task is set in 24-hour format, so the taskHour set to 16 refers to 4 PM.

const int taskHour = 16;
const int taskMinute = 5;

Other Variables

Store the day when the task last ran to ensure it only runs once a day:

int lastRunDay = -1;

Timestamp for the last NTP sync and how often you want to sync your time with the NTP server:

unsigned long lastNTPUpdate = 0;
const unsigned long ntpSyncInterval = 30 * 60 * 1000; // Sync every 30 minutes (in ms)

setup()

In the setup() you initialize the Serial communication at baud rate 115200 to print the results:

Serial.begin(115200);

These next lines connect the ESP board to your router to establish an Internet connection.

// Connect to Wi-Fi
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
  delay(500);
  Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected.");

Finally, call the syncTime() function to synchronize your board with the NTP server.

syncTime();

syncTime()

The syncTime() function connects to the NTP server, retrieves the date and time, and updates the time with the correct time zone defined earlier.

void syncTime() {
  Serial.print("Synchronizing time with NTP server...");
  configTime(0, 0, "pool.ntp.org", "time.nist.gov"); // UTC offset set to 0
  time_t now = time(nullptr);
  while (now < 24 * 3600) { // Wait until time is valid
    delay(100);
    now = time(nullptr);
  }
  Serial.println(" Time synchronized!");
  
  // Set timezone
  setenv("TZ", timezone, 1);
  tzset();

  lastNTPUpdate = millis(); // Record the time of the last sync
}

loop()

In the loop() we prepare the time and date variables. We also print the variables in the Serial Monitor for debugging purposes:

time_t now = time(nullptr);
struct tm timeinfo;
localtime_r(&now, &timeinfo);

// Current time and date
Serial.printf("Current time: %02d:%02d:%02d, Date: %04d-%02d-%02d\n",
                     timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec,
                     timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday);

This part of the code checks if it’s the exact time to run the daily task:

if (timeinfo.tm_hour == taskHour && timeinfo.tm_min == taskMinute && lastRunDay != timeinfo.tm_mday) {
  dailyTask();
  // Set the day to ensure it only runs once per day
  lastRunDay = timeinfo.tm_mday;
}

Finally, check if 30 minutes have passed, if that’s true it’s required to sync the time. We also add a delay to run the loop every second.

// Resynchronize with NTP every 30 minutes
if (millis() - lastNTPUpdate > ntpSyncInterval) {
  syncTime();
}

delay(1000);  // Run loop every second

dailyTask()

You can modify the dailyTask() function to add the desired code for your project that executes at the specified time:

void dailyTask() {
  Serial.println("#########\nDoing daily task...\n#########");
  // ENTER YOUR TASK HERE
}

Note: You can apply the same logic using an external RTC module if you don’t have access to the internet or if you don’t want to rely on the ESP32/ESP8266 internal clock. You can use a DS3231 or DS1307 RTC module.

Demonstration

At the moment the code is only printing a message in the Arduino IDE Serial Monitor when the task runs. You should modify the code to perform any other useful task. Here’s an example of how it should look like:

ESP32 ESP8266 NodeMCU Run Daily Task Arduino IDE Demonstration

ESP32/ESP8266 Run Multiple Daily Tasks – Code

Based on the previous example, you can duplicate some sections of the code to run multiple daily tasks at a specific time of day. Basically, you add two target times and check whether each task has already run for the current day using two variables. Here’s the full example:

/*  
  Rui Santos & Sara Santos - Random Nerd Tutorials
  https://RandomNerdTutorials.com/esp32-esp8266-run-daily-task/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <Arduino.h>
#if defined(ESP32)
  #include <WiFi.h>
#elif defined(ESP8266)
  #include <ESP8266WiFi.h>
#endif
#include <time.h>

// Replace with your network credentials
const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Timezone string for your region, example: https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
const char* timezone = "WET0WEST,M3.5.0/1,M10.5.0"; // WET0WEST,M3.5.0/1,M10.5.0 = Europe/Lisbon

// Time that the daily task runs in 24 hour format
const int task1Hour = 8;    // Task 1 at 8:15 AM
const int task1Minute = 15;

const int task2Hour = 18;   // Task 2 at 6:45 PM
const int task2Minute = 45;
  
// Store the day when the task last ran to ensure it only runs once per day
int lastRunDayTask1 = -1;
int lastRunDayTask2 = -1;

unsigned long lastNTPUpdate = 0; // Timestamp for the last NTP sync
const unsigned long ntpSyncInterval = 30 * 60 * 1000; // Sync every 30 minutes (in ms)

void syncTime() {
  Serial.print("Synchronizing time with NTP server...");
  configTime(0, 0, "pool.ntp.org", "time.nist.gov"); // UTC offset set to 0
  time_t now = time(nullptr);
  while (now < 24 * 3600) { // Wait until time is valid
    delay(100);
    now = time(nullptr);
  }
  Serial.println(" Time synchronized!");
  
  // Set timezone
  setenv("TZ", timezone, 1);
  tzset();

  lastNTPUpdate = millis(); // Record the time of the last sync
}

void setup() {
  Serial.begin(115200);
  
  // Connect to Wi-Fi
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected.");

  syncTime();
}

void loop() {
  time_t now = time(nullptr);
  struct tm timeinfo;
  localtime_r(&now, &timeinfo);

  // Current time and date
  Serial.printf("Current time: %02d:%02d:%02d, Date: %04d-%02d-%02d\n",
              timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec,
              timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday);

  // Check if it's time to run the daily task #1
  if (timeinfo.tm_hour == task1Hour && timeinfo.tm_min == task1Minute && lastRunDayTask1 != timeinfo.tm_mday) {
    dailyTask1();
    // Set the day to ensure it only runs once per day
    lastRunDayTask1 = timeinfo.tm_mday;
  }

  // Check if it's time to run the daily task #2
  if (timeinfo.tm_hour == task2Hour && timeinfo.tm_min == task2Minute && lastRunDayTask2 != timeinfo.tm_mday) {
    dailyTask2();
    // Set the day to ensure it only runs once per day
    lastRunDayTask2 = timeinfo.tm_mday;
  }

  // Resynchronize with NTP every 30 minutes
  if (millis() - lastNTPUpdate > ntpSyncInterval) {
    syncTime();
  }

  delay(1000); // Run loop every second
}

void dailyTask1() {
  Serial.println("#########\nDoing daily task #1...\n#########");
  // ENTER YOUR TASK HERE
}

void dailyTask2() {
  Serial.println("#########\nDoing daily task #2...\n#########");
  // ENTER YOUR TASK HERE
}

View raw code

Don’t forget to insert your network credentials, time zone, desired tasks’ time and modify the dailyTask functions to run your daily task. By default, with this new code, you’ll have task #1 running at 8:15 AM and task #2 at 6:45 PM.

Wrapping Up

This quick guide taught you how to create a simple code that can run one or multiple tasks with your ESP boards at a desired time.

We hope you found this tutorial useful. We have other tutorials related to time that you may like:

Learn more about the ESP32 with our resources:

Thank you for reading.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

4 thoughts on “ESP32/ESP8266: Run Daily Task at Specific Time (Arduino IDE)”

  1. I wonder if you could create a similar tutorial that implements the ESP32 deep sleep capability with a 32.768kHz crystal on GPIO XTAL32 pins to keep the RTC from drifting. Then the ESP32 could wake up at precisely the right time and send a message. Of course, it would need to occasionally synchronize with the NTP server because even a crystal will drift (but not by very much).

    Reply
  2. Hello
    My internet router can also be addressed as an NTP server on my FritzBox. The query on its IP address works flawlessly. The router also retrieves the data from an NTP server and makes it available on the home network.

    Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.