ESP8266 NodeMCU: Руководство по модулю часов реального времени DS1307 (RTC) (Arduino IDE)
Узнайте, как использовать модуль часов реального времени DS1307 с ESP8266. Мы рассмотрим, как установить и прочитать время. Вы научитесь синхронизировать время с локальным временем ESP8266 и периодически с NTP-сервером. Мы также рассмотрим, как работать с часовыми поясами и летним временем. Наконец, мы создадим цифровые часы на ESP8266 с использованием модуля RTC DS1307 и OLED-дисплея.
Примечание: это руководство также совместимо с 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 (для отслеживания времени) и EEPROM AT24C32 (для постоянного хранения данных). Его также можно запрограммировать на генерацию прямоугольных сигналов различной частоты. Мы будем использовать только функции чипа DS1307 для хронометража.
Держатель батареи DS1307
Он поставляется с держателем батареи для подключения батарейки CR2032 для точного хронометража. В случае отключения питания он по-прежнему может точно отслеживать время.
Этот модуль также имеет возможность подключения датчика температуры DS18B20. После подключения этого датчика к модулю вы можете получать температуру с вывода DS модуля.
I2C-адрес модуля DS1307 RTC
По умолчанию адрес DS1307 RTC — 0x68, а EEPROM, подключённый к модулю — 0x50. Вы можете запустить скетч I2C-сканера для проверки адресов.
Распиновка модуля 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
Вот список компонентов, необходимых для этого руководства:
Модуль DS1307 RTC или модуль DS3231
OLED-дисплей (опционально)
Мы будем использовать только выводы 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 |
Работа с RTC
Использование модуля RTC в ваших проектах всегда требует двух важных шагов.
Установка текущего времени: вы можете сделать это вручную, вставив текущее время (или другое желаемое время) в код; локальное время системы; или получить время с NTP-сервера.
Сохранение времени: чтобы убедиться, что RTC сохраняет правильное время, даже при потере питания, он должен быть подключён к батарее. Модули RTC поставляются с держателем батареи, обычно для монетной батарейки.
Установка библиотеки RTClib
Существует несколько библиотек для работы с RTC DS1307. Мы будем использовать RTClib от Adafruit, которая совместима с модулями RTC DS1307, DS3231 и PCF8523. Кроме того, эта библиотека также совместима с платами ESP8266.
В Arduino IDE перейдите в Sketch > Include Library > Manage Libraries. Найдите RTClib и установите библиотеку от Adafruit. Мы используем версию 2.1.4.
Установка и чтение времени
Следующий пример устанавливает время на часах 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 и будет отображать текущее время каждые три секунды.
Синхронизация 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: флаг летнего времени;
Чтобы узнать больше о получении времени с 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 секунд.
Каждый час он будет повторно синхронизировать время.
Настройка летнего времени и часового пояса
Даже если вы настроите смещение для получения времени вашего часового пояса, оно не будет учитывать, активно ли летнее время или нет (когда час меняется). Существует способ получить время для вашего конкретного часового пояса с учётом летнего времени, установив переменную окружения 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 NodeMCU с модулем RTC и OLED-дисплеем (с часовым поясом и летним временем)
Теперь мы можем легко создать цифровые часы на ESP8266, которые всегда показывают правильное время, синхронизированное с вашим часовым поясом и с учётом летнего времени.
Подключение схемы
Добавьте 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, вы можете использовать следующее руководство в качестве справки.
Демонстрация
После загрузки кода цифровые часы готовы. Теперь вы можете сделать красивый корпус для более законченного вида.
Заключение
В этом руководстве вы узнали, как использовать модуль RTC DS1307 с ESP8266. Вы научились устанавливать и считывать его время. Вы также узнали, как периодически синхронизировать время с NTP-сервером, чтобы RTC всегда показывал точное время. Кроме того, мы рассмотрели, как настроить время для вашего часового пояса и учесть летнее время.
Наконец, мы создали цифровые часы с использованием OLED-дисплея. В качестве альтернативы вы можете использовать, например, LCD. Вы также можете объединить этот проект со следующим:
У нас есть руководства для более чем 20 модулей с ESP8266, которые могут быть полезны:
Если вы хотите узнать больше о ESP8266, ознакомьтесь с нашими ресурсами:
Спасибо за чтение.