ESP8266 NodeMCU: Руководство по модулю часов реального времени DS1307 (RTC) (Arduino IDE)

Узнайте, как использовать модуль часов реального времени DS1307 с ESP8266. Мы рассмотрим, как установить и прочитать время. Вы научитесь синхронизировать время с локальным временем ESP8266 и периодически с NTP-сервером. Мы также рассмотрим, как работать с часовыми поясами и летним временем. Наконец, мы создадим цифровые часы на ESP8266 с использованием модуля RTC DS1307 и OLED-дисплея.

ESP8266 NodeMCU: Руководство по модулю часов реального времени DS1307 RTC Arduino IDE

Примечание: это руководство также совместимо с DS3231 RTC, достаточно изменить одну строку кода в примерах.

Оглавление

В этом руководстве мы рассмотрим следующие темы:

Знакомство с модулями часов реального времени (RTC)

Модули RTC, такие как DS3231 и DS1307, имеют собственные миниатюрные часы внутри для самостоятельного отслеживания времени. Обычно они поставляются с держателем батареи для подключения батарейки, чтобы они продолжали работать, даже если ESP8266 перезагрузится или потеряет питание.

DS3231 и DS1307 являются одними из самых популярных вариантов для использования с микроконтроллерами. Оба совместимы с ESP8266 и обмениваются данными по протоколу I2C. DS3231 более точный, поскольку оснащён датчиком температуры и обеспечивает результаты с температурной компенсацией. Тем не менее, DS1307 также очень точен и подходит для большинства приложений, которым необходимо отслеживать время. В этом руководстве мы рассмотрим модуль RTC DS1307.

Знакомство с модулем DS1307 RTC

В этом руководстве мы будем использовать модуль RTC DS1307, но если у вас есть DS3231, большая часть предоставленной информации применима к обоим модулям. Кроме того, весь код должен быть совместим с обоими модулями с минимальными изменениями. (В ближайшем будущем мы создадим руководство для модуля RTC DS3231).

Модуль DS1307 RTC

Модуль DS1307 RTC поставляется с чипом DS1307 (для отслеживания времени) и EEPROM AT24C32 (для постоянного хранения данных). Его также можно запрограммировать на генерацию прямоугольных сигналов различной частоты. Мы будем использовать только функции чипа DS1307 для хронометража.

Держатель батареи DS1307

Он поставляется с держателем батареи для подключения батарейки CR2032 для точного хронометража. В случае отключения питания он по-прежнему может точно отслеживать время.

Модуль DS1307 RTC с держателем батареи Модуль DS1307 RTC с держателем батареи и батарейкой

Этот модуль также имеет возможность подключения датчика температуры DS18B20. После подключения этого датчика к модулю вы можете получать температуру с вывода DS модуля.

I2C-адрес модуля DS1307 RTC

По умолчанию адрес DS1307 RTC — 0x68, а EEPROM, подключённый к модулю — 0x50. Вы можете запустить скетч I2C-сканера для проверки адресов.

Получение I2C-адресов модуля RTC

Распиновка модуля DS1307 RTC

В следующей таблице кратко описана распиновка модуля DS1307 RTC.

SQ

Выход для прямоугольных сигналов: 1 Гц, 4,096 кГц, 8,192 кГц или 32,768 кГц (мы не будем использовать)

DS

Выход для показаний температуры при подключённом DS18B20 (мы не будем использовать)

SCL

Вывод SCL для I2C

SDA

Вывод SDA для I2C

VCC

Питание модуля (3,3В или 5В)

GND

GND

BAT

Вход резервного питания (мы не будем использовать) или используйте стандартный держатель батареи

Подключение модуля DS1307 RTC к ESP8266 NodeMCU

Вот список компонентов, необходимых для этого руководства:

Мы будем использовать только выводы I2C и питания для подключения модуля RTC к ESP8266. Мы подключим SCL к GPIO 5 (D1); а SDA к GPIO 4 (D2). Вы можете использовать любые другие подходящие выводы I2C, если измените их в коде. Вы можете использовать следующую таблицу в качестве справки или посмотреть на принципиальную схему.

Модуль RTC

ESP8266

SCL

GPIO 5 (D1)

SDA

GPIO 4 (D2)

VCC

3V3

GND

GND

Схема подключения ESP8266 с модулем DS1307 RTC

Работа с RTC

Использование модуля RTC в ваших проектах всегда требует двух важных шагов.

  1. Установка текущего времени: вы можете сделать это вручную, вставив текущее время (или другое желаемое время) в код; локальное время системы; или получить время с NTP-сервера.

  2. Сохранение времени: чтобы убедиться, что RTC сохраняет правильное время, даже при потере питания, он должен быть подключён к батарее. Модули RTC поставляются с держателем батареи, обычно для монетной батарейки.

Установка библиотеки RTClib

Существует несколько библиотек для работы с RTC DS1307. Мы будем использовать RTClib от Adafruit, которая совместима с модулями RTC DS1307, DS3231 и PCF8523. Кроме того, эта библиотека также совместима с платами ESP8266.

В Arduino IDE перейдите в Sketch > Include Library > Manage Libraries. Найдите RTClib и установите библиотеку от Adafruit. Мы используем версию 2.1.4.

Установка библиотеки RTCLib в Arduino IDE

Установка и чтение времени

ESP8266 NodeMCU с модулем DS1307 RTC

Следующий пример устанавливает время на часах RTC, а затем считывает время в цикле каждые три секунды. Этот код показывает два разных способа установки времени: синхронизацию RTC с системным временем (дата и время компиляции скетча) и установку конкретной даты и времени вручную, записав их самостоятельно в коде.

/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp8266-nodemcu-ds1307-real-time-clock-rtc-arduino/
  Based on the RTClib Library examples: github.com/adafruit/RTClib/blob/master/examples
*/

// Date and time functions using a DS1307 RTC connected via I2C and Wire lib
#include "RTClib.h"

RTC_DS1307 rtc;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

void setup () {
  Serial.begin(115200);

  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    while (1) delay(10);
  }

  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running, let's set the time!");
    // When time needs to be set on a new device, or after a power loss, the
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    //rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }

  // When time needs to be re-set on a previously configured device, the
  // following line sets the RTC to the date & time this sketch was compiled
  //rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  // This line sets the RTC with an explicit date & time, for example to set
  // January 21, 2014 at 3am you would call:
  //rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}

void loop () {
  // Get the current time from the RTC
  DateTime now = rtc.now();

  // Getting each time field in individual variables
  // And adding a leading zero when needed;
  String yearStr = String(now.year(), DEC);
  String monthStr = (now.month() < 10 ? "0" : "") + String(now.month(), DEC);
  String dayStr = (now.day() < 10 ? "0" : "") + String(now.day(), DEC);
  String hourStr = (now.hour() < 10 ? "0" : "") + String(now.hour(), DEC);
  String minuteStr = (now.minute() < 10 ? "0" : "") + String(now.minute(), DEC);
  String secondStr = (now.second() < 10 ? "0" : "") + String(now.second(), DEC);
  String dayOfWeek = daysOfTheWeek[now.dayOfTheWeek()];

  // Complete time string
  String formattedTime = dayOfWeek + ", " + yearStr + "-" + monthStr + "-" + dayStr + " " + hourStr + ":" + minuteStr + ":" + secondStr;

  // Print the complete formatted time
  Serial.println(formattedTime);

  Serial.println();
  delay(3000);
}

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

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

Начните с подключения библиотеки RTCLib.

#include "RTClib.h"

Затем создайте объект RTC_DS1307 с именем rtc.

RTC_DS1307 rtc;

Если вы используете модуль RTC DS3231, используйте вместо этого следующую строку.

RTC_DS3231 rtc;

Затем вы создаёте массив char с днями недели.

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

Вы можете получить доступ к каждому дню по его индексу. Например:

  • daysOfTheWeek[0] вернёт «Sunday».

  • daysOfTheWeek[1] вернёт «Monday».

В setup() инициализируйте Serial Monitor.

Serial.begin(115200);

Инициализируйте модуль RTC следующим образом:

if (! rtc.begin()) {
  Serial.println("Couldn't find RTC");
  Serial.flush();
  while (1) delay(10);
}

Проверка состояния RTC

Затем проверьте, ведёт ли RTC отсчёт времени с помощью функции isrunning(). Если он не работает, потому что это новое устройство или потому что резервная батарея разрядилась, мы выведем сообщение в Serial Monitor и установим время.

if (! rtc.isrunning()) {
   Serial.println("RTC is NOT running, let's set the time!");

Установка времени

Для установки времени на RTC мы можем использовать метод adjust() нашего объекта rtc. Следующая строка устанавливает дату и время RTC на текущую дату и время, когда этот скетч был последний раз скомпилирован.

rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

__DATE__ и __TIME__ — это макросы, которые предоставляют текущую дату и время на момент компиляции.

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

// January 21, 2014 at 3am you would call:
rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));

Если вам нужно переустановить время на ранее настроенном устройстве, вы можете вызвать одну из двух предыдущих строк для установки времени без проверки, работает ли оно или нет (это закомментировано в коде).

// When time needs to be re-set on a previously configured device, the
// following line sets the RTC to the date & time this sketch was compiled
//rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
//rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));

Получение даты и времени

В loop() мы получаем дату и время каждые три секунды и выводим их в Serial Monitor.

Мы вызываем rtc.now() для получения текущей даты и времени из модуля RTC.

DateTime now = rtc.now();

Он возвращает объект DateTime, содержащий значения текущего года, месяца, дня, часа, минуты и секунды.

Для доступа к каждому полю даты и времени мы можем использовать следующие методы:

now.year()

Получает текущий год (например, 2024)

now.month()

Получает текущий месяц (1–12)

now.day()

Получает текущий день месяца (1–31)

now.dayOfTheWeek()

Получает день недели (0-6), где 0 — воскресенье, а 6 — суббота

now.hour()

Получает текущий час (0–23)

now.minute()

Получает текущую минуту (0–59)

now.second()

Получает текущую секунду (0–59)

Для преобразования результата в строку мы можем использовать метод String(). Мы также передаём DEC в качестве второго аргумента метода String() для получения десятичного числа.

String yearStr = String(now.year(), DEC);

В случае месяца, дня, часа, минуты и секунды мы добавляем ведущий ноль, когда число меньше 10. Таким образом, вместо, например: 3:5:6 (что выглядит странно для формата времени), вы получите 03:05:06.

String monthStr = (now.month() < 10 ? "0" : "") + String(now.month(), DEC);
String dayStr = (now.day() < 10 ? "0" : "") + String(now.day(), DEC);
String hourStr = (now.hour() < 10 ? "0" : "") + String(now.hour(), DEC);
String minuteStr = (now.minute() < 10 ? "0" : "") + String(now.minute(), DEC);
String secondStr = (now.second() < 10 ? "0" : "") + String(now.second(), DEC);

Для получения названия дня недели мы используем массив daysOfTheWeek, созданный в начале кода.

String dayOfWeek = daysOfTheWeek[now.dayOfTheWeek()];

В конце мы объединяем все поля времени в переменную и отображаем её в Serial Monitor.

// Complete time string
String formattedTime = dayOfWeek + ", " + yearStr + "-" + monthStr + "-" + dayStr + " " + hourStr + ":" + minuteStr + ":" + secondStr;

// Print the complete formatted time
Serial.println(formattedTime);

Тестирование примера

При подключённом RTC к ESP8266 загрузите код на вашу плату.

Откройте Serial Monitor со скоростью 115200 бод. ESP8266 установит время RTC и будет отображать текущее время каждые три секунды.

ESP8266 с модулем RTC, установка и получение времени

Синхронизация RTC с NTP-сервером

Следующий пример аналогичен предыдущему, но синхронизирует время RTC с NTP-сервером после подключения ESP8266 к интернету. Этот метод полезен, если необходимо сбросить или скорректировать время без ручного ввода, например, после замены монетной батарейки RTC или если по какой-либо причине модуль RTC выходит из строя. В этом конкретном примере мы также добавляем возможность синхронизации времени с NTP-сервером каждый час для предотвращения дрейфа со временем (периодическая синхронизация должна быть настроена в зависимости от требований вашего проекта).

/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp8266-nodemcu-ds1307-real-time-clock-rtc-arduino/
  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 <ESP8266WiFi.h>
#include <time.h>
#include <RTClib.h>

// Enter your Wi-Fi credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// NTP server details
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 0;  // Offset for GMT in seconds
const int daylightOffset_sec = 3600;  // Daylight savings time in seconds

// RTC object (for DS1307 or DS3231)
RTC_DS1307 rtc;  // Change to RTC_DS1307 for DS1307 module

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

// Global timeinfo struct and last sync timestamp
struct tm timeinfo;
unsigned long lastSyncMillis = 0;  // Last sync time in milliseconds

void setup() {
  Serial.begin(115200);
  initWiFi();

  // Initialize RTC
  if (!rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }

  // Sync the RTC at startup
  syncTime();
}

void loop() {
  checkTimeAndSync();  // Check if 1 hour has passed and sync if necessary

  // Get current time from RTC
  DateTime now = rtc.now();

  // Getting each time field in individual variables
  String yearStr = String(now.year(), DEC);
  String monthStr = (now.month() < 10 ? "0" : "") + String(now.month(), DEC);
  String dayStr = (now.day() < 10 ? "0" : "") + String(now.day(), DEC);
  String hourStr = (now.hour() < 10 ? "0" : "") + String(now.hour(), DEC);
  String minuteStr = (now.minute() < 10 ? "0" : "") + String(now.minute(), DEC);
  String secondStr = (now.second() < 10 ? "0" : "") + String(now.second(), DEC);
  String dayOfWeek = daysOfTheWeek[now.dayOfTheWeek()];

  // Complete time string
  String formattedTime = dayOfWeek + ", " + yearStr + "-" + monthStr + "-" + dayStr + " " + hourStr + ":" + minuteStr + ":" + secondStr;

  // Print the complete formatted time
  Serial.println(formattedTime);

  Serial.println();
  delay(10000);
}

void initWiFi() {
  WiFi.begin(ssid, password);  // Connect to WiFi
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi connected.");
}

void syncTime() {
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);  // Configure time with NTP server
  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
  }
  Serial.println("\nESP8266 Time synchronized with NTP server.");

  // Format the current time as a string
  char timeStr[64];
  strftime(timeStr, sizeof(timeStr), "%A, %B %d %Y %H:%M:%S", &timeinfo);
  Serial.print("Current time: ");
  Serial.println(timeStr);

  // Sync the RTC with the NTP time
  rtc.adjust(DateTime(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
                      timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec));

  lastSyncMillis = millis();  // Record the last sync time in milliseconds
}

void checkTimeAndSync() {
  // Check if 1 hour has passed since the last sync (1 hour = 3600000 milliseconds)
  if (millis() - lastSyncMillis >= 3600000) {
    Serial.println("Synchronizing time with NTP...");
    syncTime();
  }
}

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

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

Этот код аналогичен предыдущему, но использует NTP-сервер для синхронизации времени. Для получения времени с NTP-сервера ESP8266 должен быть подключён к интернету.

Сначала вам нужно подключить следующие библиотеки.

#include <ESP8266WiFi.h>
#include <time.h>
#include <RTClib.h>

Затем вам нужно вставить ваши сетевые данные в следующие строки.

// WiFi credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Далее у нас есть настройки NTP-сервера. NTP-сервер возвращает время в GMT. Если вы находитесь в другом часовом поясе, вы можете добавить смещение часового пояса в переменную gmtOffset_sec в секундах. Также добавьте летнее время в секундах в переменную daylightOffset_sec, если необходимо.

// NTP server details
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 0;  // Offset for GMT in seconds
const int daylightOffset_sec = 3600;  // Daylight savings time in seconds

Мы создаём структуру tm с именем timeinfo для сохранения времени с NTP-сервера.

struct tm timeinfo;

Структура tm содержит календарную дату и время, разбитые на компоненты:

  • tm_sec: секунды после минуты;

  • tm_min: минуты после часа;

  • tm_hour: часы с полуночи;

  • tm_mday: день месяца;

  • tm_year: годы с 1900;

  • tm_wday: дни с воскресенья;

  • tm_yday: дни с 1 января;

  • tm_isdst: флаг летнего времени;

  • документация по структуре tm.

Чтобы узнать больше о получении времени с NTP-сервера с помощью ESP8266, ознакомьтесь с этим руководством: Получение даты и времени с ESP8266 NodeMCU NTP Client-Server.

Мы создаём переменную для сохранения времени, прошедшего с момента последней синхронизации.

unsigned long lastSyncMillis = 0;  // Last sync time in milliseconds

В setup() мы инициализируем Serial Monitor и подключаем ESP8266 к Wi-Fi.

void setup() {
    Serial.begin(115200);
    initWiFi();

Мы инициализируем модуль RTC.

// Initialize RTC
if (!rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
}

И наконец, мы вызываем функцию syncTime(), объявленную в конце кода, для синхронизации времени.

// Sync the RTC at startup
syncTime();

Функция syncTime()

В функции syncTime() мы получаем время с NTP-сервера и сохраняем его в переменной timeinfo.

configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);  // Configure time with NTP server
if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
}

Затем мы вызываем метод rtc.adjust() и передаём в качестве аргументов поля времени из переменной timeinfo. Это синхронизирует время RTC со временем NTP-сервера.

// Sync the RTC with the NTP time
rtc.adjust(DateTime(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
                     timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec));

Примечание: timeinfo.tm_year содержит количество лет с 1900 года. Поэтому нам нужно добавить 1900, чтобы получить текущий год.

Наконец, мы обновляем lastSyncMillis() текущим временем.

// Sync the RTC at startup
syncTime();

loop()

В loop() мы начинаем с вызова функции checkTimeAndSync().

checkTimeAndSync();  // Check if 1 hour has passed and sync if necessary

Эта функция проверяет, прошёл ли один час с момента последней синхронизации, и синхронизирует время при необходимости.

void checkTimeAndSync() {
    // Check if 1 hour has passed since the last sync (1 hour = 3600000 milliseconds)
    if (millis() - lastSyncMillis >= 3600000) {
        Serial.println("Synchronizing time with NTP...");
        syncTime();
    }
}

Далее в loop(), после синхронизации времени, мы получаем время из модуля RTC и выводим его в Serial Monitor каждые 10 секунд.

// Get current time from RTC
DateTime now = rtc.now();

// Getting each time field in individual variables
String yearStr = String(now.year(), DEC);
String monthStr = (now.month() < 10 ? "0" : "") + String(now.month(), DEC);
String dayStr = (now.day() < 10 ? "0" : "") + String(now.day(), DEC);
String hourStr = (now.hour() < 10 ? "0" : "") + String(now.hour(), DEC);
String minuteStr = (now.minute() < 10 ? "0" : "") + String(now.minute(), DEC);
String secondStr = (now.second() < 10 ? "0" : "") + String(now.second(), DEC);
String dayOfWeek = daysOfTheWeek[now.dayOfTheWeek()];

// Complete time string
String formattedTime = dayOfWeek + ", " + yearStr + "-" + monthStr + "-" + dayStr + " " + hourStr + ":" + minuteStr + ":" + secondStr;

// Print the complete formatted time
Serial.println(formattedTime);

Serial.println();
delay(10000);

Тестирование кода

Загрузите предыдущий код на ESP8266. Он подключится к интернету, получит время с NTP-сервера и синхронизирует время модуля RTC.

После этого он будет отображать дату и время каждые 10 секунд.

ESP8266 RTC синхронизация времени с NTP-сервером

Каждый час он будет повторно синхронизировать время.

Настройка летнего времени и часового пояса

Даже если вы настроите смещение для получения времени вашего часового пояса, оно не будет учитывать, активно ли летнее время или нет (когда час меняется). Существует способ получить время для вашего конкретного часового пояса с учётом летнего времени, установив переменную окружения TZ в правильное значение.

Следующий пример аналогичен предыдущему, но вы всегда будете получать правильное время с учётом вашего часового пояса (и летнего времени).

/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp8266-nodemcu-ds1307-real-time-clock-rtc-arduino/
  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 <ESP8266WiFi.h>
#include <time.h>
#include <RTClib.h>  // For DS3231 or DS1307 RTC module

// Enter your Wi-Fi credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// NTP server and timezone details
const char* ntpServer = "pool.ntp.org";
const char* timezone = "WET0WEST,M3.5.0/1,M10.5.0";
// Check list of timezones here: https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv

// RTC object (change to RTC_DS3231 for DS3231 module)
RTC_DS1307 rtc;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

// Track last sync time
struct tm timeinfo;
unsigned long lastSyncMillis = 0;  // Last sync time in milliseconds

void setup() {
  Serial.begin(115200);
  initWiFi();

  // Initialize RTC
  if (!rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);  // Stop if RTC is not found
  }

  // Sync the RTC at startup
  syncTime();
}

void loop() {
  checkTimeAndSync();  // Check if 1 hour has passed and sync if necessary

  // Get current time from RTC
  DateTime now = rtc.now();

  // Format the time as strings
  String yearStr = String(now.year());
  String monthStr = (now.month() < 10 ? "0" : "") + String(now.month());
  String dayStr = (now.day() < 10 ? "0" : "") + String(now.day());
  String hourStr = (now.hour() < 10 ? "0" : "") + String(now.hour());
  String minuteStr = (now.minute() < 10 ? "0" : "") + String(now.minute());
  String secondStr = (now.second() < 10 ? "0" : "") + String(now.second());
  String dayOfWeek = daysOfTheWeek[now.dayOfTheWeek()];

  // Print the formatted time
  String formattedTime = dayOfWeek + ", " + yearStr + "-" + monthStr + "-" + dayStr + " " + hourStr + ":" + minuteStr + ":" + secondStr;
  Serial.println(formattedTime);

  delay(10000);
}

void initWiFi() {
  Serial.print("Connecting to WiFi");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi connected.");
}

void syncTime() {
  // Configure time with NTP server and get time info
  configTime(0, 0, ntpServer);
  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
  }

  // Configure timezone for DST using POSIX rule
  Serial.println("Configuring timezone and daylight saving time");
  setenv("TZ", timezone, 1);
  tzset();
  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain adjusted time");
    return;
  }

  Serial.println("\nTime synchronized with NTP server with timezone and DST.");
  char timeStr[64];
  strftime(timeStr, sizeof(timeStr), "%A, %B %d %Y %H:%M:%S", &timeinfo);
  Serial.print("Current time: ");
  Serial.println(timeStr);

  // Update the RTC time
  rtc.adjust(DateTime(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
                      timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec));

  lastSyncMillis = millis();  // Update last sync time
}

void checkTimeAndSync() {
  // Sync with NTP server every 1 hour (3600000 ms)
  if (millis() - lastSyncMillis >= 3600000) {
    Serial.println("Synchronizing time with NTP...");
    syncTime();
  }
}

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

По сути, мы получаем время с NTP-сервера, конвертируем его в ваш часовой пояс, устанавливая переменную окружения TZ, а затем устанавливаем модуль RTC с этим временем. Каждый час модуль RTC повторно синхронизирует своё время (вы можете изменить это на более длительный период).

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

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

Установите строку вашего часового пояса в следующей переменной. Список строк часовых поясов можно найти здесь. Наш — Europe/Lisbon.

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

В функции syncTime() мы сначала получаем время с NTP-сервера.

void syncTime() {

// Configure time with NTP server and get time info
configTime(0, 0, ntpServer);
if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
}

Для преобразования времени в конкретный часовой пояс используйте функцию setenv() для установки переменной окружения TZ (timezone) следующим образом, где timezone — ваша строка часового пояса.

setenv("TZ", timezone, 1);

После этого вызовите функцию tzset(), чтобы изменения вступили в силу.

tzset();

После этого снова вызовите функцию getLocalTime(), чтобы получить время, скорректированное для вашего часового пояса.

if (!getLocalTime(&timeinfo)) {
  Serial.println("Failed to obtain adjusted time");
  return;
}

Теперь, когда у вас есть скорректированное время, вы можете синхронизировать время модуля RTC с помощью rtc.adjust().

rtc.adjust(DateTime(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
                    timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec));

Остальная часть кода работает точно так же, как и предыдущий пример.

Тестирование примера

Теперь, после загрузки кода, вы должны получить правильное время для вашего часового пояса с учётом летнего времени.

ESP8266 с модулем RTC, настройка часового пояса и летнего времени

Часы ESP8266 NodeMCU с модулем RTC и OLED-дисплеем (с часовым поясом и летним временем)

Теперь мы можем легко создать цифровые часы на ESP8266, которые всегда показывают правильное время, синхронизированное с вашим часовым поясом и с учётом летнего времени.

Часы ESP8266 с DS1307 RTC и OLED-дисплеем

Подключение схемы

Добавьте OLED-дисплей в вашу схему. Подключите его, как показано в таблице ниже.

OLED-дисплей

ESP8266

SDA

GPIO 4 (D2)

SCL

GPIO 5 (D1)

GND

GND

VCC

3V3

Часы ESP8266 NodeMCU с модулем RTC и OLED-дисплеем — Код

Загрузите следующий код на вашу плату. Настройте переменную timezone с вашей строкой часового пояса и вставьте ваши сетевые учётные данные.

/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp8266-nodemcu-ds1307-real-time-clock-rtc-arduino/
  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 <ESP8266WiFi.h>
#include <time.h>
#include <RTClib.h>          // For DS3231 or DS1307 RTC module
#include <Adafruit_SSD1306.h>

// Enter your Wi-Fi credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// NTP server and timezone details
const char* ntpServer = "pool.ntp.org";
const char* timezone = "WET0WEST,M3.5.0/1,M10.5.0";
// Check list of timezones here: https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv

// RTC object (change to RTC_DS3231 for DS3231 module)
RTC_DS1307 rtc;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

// OLED display setup
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// Track last sync time
struct tm timeinfo;
unsigned long lastSyncMillis = 0;

void setup() {
  Serial.begin(115200);
  initWiFi();

  // Initialize the OLED display
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    while (1); // Stop if display initialization fails
  }
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE); // Draw white text
  display.setCursor(0, 0);   // Start at top-left corner
  display.display();

  // Initialize RTC
  if (!rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }

  // Sync the RTC at startup
  syncTime();
}

void loop() {
  checkTimeAndSync(); // Check if 1 hour has passed and sync if necessary

  // Get current time from RTC
  DateTime now = rtc.now();

  // Format the time as strings
  String yearStr = String(now.year());
  String monthStr = (now.month() < 10 ? "0" : "") + String(now.month());
  String dayStr = (now.day() < 10 ? "0" : "") + String(now.day());
  String hourStr = (now.hour() < 10 ? "0" : "") + String(now.hour());
  String minuteStr = (now.minute() < 10 ? "0" : "") + String(now.minute());
  String secondStr = (now.second() < 10 ? "0" : "") + String(now.second());
  String dayOfWeek = daysOfTheWeek[now.dayOfTheWeek()];

  // Construct the formatted time
  String formattedTime = dayOfWeek + "\n" + yearStr + "-" + monthStr + "-" + dayStr +
                          "\n" + hourStr + ":" + minuteStr + ":" + secondStr;

  // Display the formatted time on the OLED
  display.clearDisplay();
  display.setCursor(0, 0);
  display.println(formattedTime);
  display.display();

  delay(1000); // Update every second
}

void initWiFi() {
  Serial.print("Connecting to WiFi");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi connected.");
}

void syncTime() {
  // Configure time with NTP server and get time info
  configTime(0, 0, ntpServer);
  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
  }

  // Configure timezone for DST using POSIX rule
  Serial.println("Configuring timezone and daylight saving time");
  setenv("TZ", timezone, 1);
  tzset();
  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain adjusted time");
    return;
  }

  Serial.println("\nTime synchronized with NTP server with timezone and DST.");
  char timeStr[64];
  strftime(timeStr, sizeof(timeStr), "%A, %B %d %Y %H:%M:%S", &timeinfo);
  Serial.print("Current time: ");
  Serial.println(timeStr);

  // Update the RTC with NTP time
  rtc.adjust(DateTime(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
                      timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec));

  lastSyncMillis = millis(); // Update last sync time
}

void checkTimeAndSync() {
  // Sync with NTP server every 1 hour (3600000 ms)
  if (millis() - lastSyncMillis >= 3600000) {
    Serial.println("Synchronizing time with NTP...");
    syncTime();
  }
}

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

Мы не будем объяснять, как работает этот код. Большая часть была объяснена в предыдущих примерах. Чтобы узнать, как подключить OLED-дисплей к ESP8266, вы можете использовать следующее руководство в качестве справки.

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

После загрузки кода цифровые часы готовы. Теперь вы можете сделать красивый корпус для более законченного вида.

Цифровые часы ESP32 с RTC и OLED

Заключение

В этом руководстве вы узнали, как использовать модуль RTC DS1307 с ESP8266. Вы научились устанавливать и считывать его время. Вы также узнали, как периодически синхронизировать время с NTP-сервером, чтобы RTC всегда показывал точное время. Кроме того, мы рассмотрели, как настроить время для вашего часового пояса и учесть летнее время.

Наконец, мы создали цифровые часы с использованием OLED-дисплея. В качестве альтернативы вы можете использовать, например, LCD. Вы также можете объединить этот проект со следующим:

У нас есть руководства для более чем 20 модулей с ESP8266, которые могут быть полезны:

Если вы хотите узнать больше о ESP8266, ознакомьтесь с нашими ресурсами:

Спасибо за чтение.