ESP32/ESP8266: Запуск ежедневной задачи в определенное время (Arduino IDE)

В этом руководстве мы покажем, как получить дату и время с помощью ESP32 или ESP8266, чтобы запускать одну или несколько задач каждый день в точное время. Для этого приложения ваши платы ESP будут получать время с помощью протокола сетевого времени (NTP), поэтому они должны быть подключены к Интернету. Платы ESP будут программироваться с помощью Arduino IDE.

ESP32 ESP8266 NodeMCU запуск ежедневной задачи в определенное время Arduino IDE

Необходимые условия

Перед началом работы убедитесь, что у вас установлено дополнение для плат ESP32 или ESP8266 в Arduino IDE:

Для этого руководства вам понадобится только плата ESP32 или ESP8266:

NTP (Network Time Protocol)

Для отслеживания времени мы будем использовать NTP. NTP расшифровывается как Network Time Protocol (протокол сетевого времени) – это сетевой протокол для синхронизации часов между компьютерными системами. Другими словами, он используется для синхронизации времени компьютерных часов в сети.

Существуют NTP-серверы, такие как pool.ntp.org, которые любой может использовать для запроса времени в качестве клиента. В этом случае ESP32/ESP8266 является NTP-клиентом, который запрашивает время у NTP-сервера (pool.ntp.org).

NTP протокол сетевого времени ESP32 запрос времени и даты

Если вы хотите узнать больше о взаимодействии NTP-клиента и сервера с платами ESP, вы можете прочитать следующие руководства:

В этом руководстве вы будете использовать стандартную библиотеку time.h, которая поставляется вместе с фреймворком Arduino, поэтому вам не нужно устанавливать дополнительные библиотеки.

ESP32/ESP8266: Запуск ежедневной задачи в определенное время – Код

Чтобы получить дату и время с помощью плат ESP, вам не нужно устанавливать никаких библиотек. Вы просто подключаете библиотеку time.h в свой код.

Следующий код получает дату и время от NTP-сервера, выводит результаты в Serial Monitor и проверяет, была ли выполнена ежедневная задача или нет.

/*
  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
}

Просмотреть исходный код

Как работает код

Давайте кратко рассмотрим код, чтобы понять, как он работает. Сначала подключаем библиотеки для подключения к Wi-Fi и получения времени.

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

Настройка SSID и пароля

Введите ваши сетевые учетные данные в следующие переменные, чтобы ESP32/ESP8266 мог установить интернет-соединение и получить дату и время от NTP-сервера.

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

Установка переменной timezone

Затем вам нужно определить переменную timezone для получения правильного времени для вашего местоположения.

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

Например, автор живет в Порту. Часовой пояс – Europe/Lisbon. Из списка строковых переменных часовых поясов видно, что строковая переменная часового пояса для этого местоположения – WET0WEST,M3.5.0/1,M10.5.0. Вы можете проверить список строковых переменных часовых поясов здесь.

Установка часа и минуты запуска задачи

Установите час и минуту, когда вы хотите запускать ежедневную задачу. Время для ежедневной задачи устанавливается в 24-часовом формате, поэтому taskHour, установленный на 16, соответствует 4 часам дня.

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

Другие переменные

Сохраняем день, когда задача была последний раз запущена, чтобы убедиться, что она выполняется только один раз в день:

int lastRunDay = -1;

Метка времени последней синхронизации NTP и как часто вы хотите синхронизировать время с NTP-сервером:

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

setup()

В setup() вы инициализируете Serial-соединение на скорости 115200 бод для вывода результатов:

Serial.begin(115200);

Следующие строки подключают плату ESP к вашему роутеру для установления интернет-соединения.

// 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() для синхронизации вашей платы с NTP-сервером.

syncTime();

syncTime()

Функция syncTime() подключается к NTP-серверу, получает дату и время и обновляет время с правильным часовым поясом, определенным ранее.

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()

В loop() мы подготавливаем переменные времени и даты. Мы также выводим переменные в Serial Monitor для целей отладки:

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);

Эта часть кода проверяет, наступило ли точное время для запуска ежедневной задачи:

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;
}

Наконец, проверяем, прошло ли 30 минут, и если да, то необходимо синхронизировать время. Мы также добавляем задержку для запуска цикла каждую секунду.

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

delay(1000);  // Run loop every second

dailyTask()

Вы можете изменить функцию dailyTask(), чтобы добавить нужный код для вашего проекта, который будет выполняться в указанное время:

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

Примечание

Вы можете применить ту же логику, используя внешний модуль RTC, если у вас нет доступа к Интернету или если вы не хотите полагаться на внутренние часы ESP32/ESP8266. Вы можете использовать модуль RTC DS3231 или DS1307.

Демонстрация

На данный момент код только выводит сообщение в Serial Monitor Arduino IDE, когда задача выполняется. Вам следует изменить код для выполнения любой другой полезной задачи. Вот пример того, как это должно выглядеть:

ESP32 ESP8266 NodeMCU запуск ежедневной задачи Arduino IDE демонстрация

ESP32/ESP8266: Запуск нескольких ежедневных задач – Код

На основе предыдущего примера вы можете продублировать некоторые разделы кода для запуска нескольких ежедневных задач в определенное время суток. По сути, вы добавляете два целевых времени и проверяете, была ли каждая задача уже выполнена в текущий день, используя две переменные. Вот полный пример:

/*
  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
}

Просмотреть исходный код

Не забудьте ввести ваши сетевые учетные данные, часовой пояс, желаемое время выполнения задач и изменить функции dailyTask для запуска вашей ежедневной задачи. По умолчанию в этом новом коде задача #1 будет выполняться в 8:15 утра, а задача #2 – в 6:45 вечера.

Заключение

Это краткое руководство научило вас создавать простой код, который может запускать одну или несколько задач на ваших платах ESP в нужное время.

Мы надеемся, что это руководство было полезным. У нас есть другие руководства, связанные со временем, которые могут вам понравиться:

Узнайте больше об ESP32 с нашими ресурсами:

Источник: :doc:`ESP32/ESP8266: Run Daily Task at Specific Time (Arduino IDE) <../esp32-esp8266-run-daily-task/index>` – Random Nerd Tutorials, Rui Santos & Sara Santos