// Код для Arduino Nano, простые часы на дисплее MAX7219, часах DS3231 и энкодере с кнопкой
// Написан: https://vk.com/theaxial
// Для: https://vk.com/id108880598
// Создан: 27.12.2024
// Изменен: 11.01.2025
// Онлайн эмулятор: https://wokwi.com/projects/418413426427416577
#include <Arduino.h>
#include <GyverDS3231.h>
#include <GyverMAX7219.h>
#include <EncButton.h>
// Объект дисплея: матрица 4 x 1, пин CS — D10
MAX7219<4, 1, 10> display;
GyverDS3231 watch;
// Энкодер + кнопка (пины 2, 3, 4)
EncButton eb(2, 3, 4);
// Прочие переменные
byte brightness = 5; // Яркость дисплея (0..15)
bool dot = true; // Флаг для мигающей двоеточия
bool blinking = true; // Флаг включения мигания двоеточия
bool settingMode = false; // находимся ли в режиме настройки
byte settingStep = 0; // 0 — редактируем часы, 1 — редактируем минуты
byte hourSetting = 0;
byte minuteSetting = 0;
// Прерывания энкодера
void isr() {
eb.tickISR();
}
void setup() {
Serial.begin(115200);
// Включаем прерывания на пинах энкодера
attachInterrupt(0, isr, CHANGE); // пин 2 на Nano
attachInterrupt(1, isr, CHANGE); // пин 3 на Nano
eb.setEncISR(true);
// Инициализация дисплея
display.begin();
display.setConnection(GM_RIGHT_TOP_LEFT);
display.setBright(brightness);
// Инициализация RTC
Wire.begin();
watch.begin();
// watch.setBuildTime();
Serial.println("Часы запущены");
}
void loop() {
eb.tick();
// ----- Обработка яркости и кликов (если мы не в режиме настройки) -----
if (!settingMode) {
// Поворот энкодера влево
if (eb.left()) {
brightness--;
if (brightness < 1) brightness = 1;
display.setBright(brightness);
Serial.println("Bri = " + String(brightness));
}
// Поворот энкодера вправо
if (eb.right()) {
brightness++;
if (brightness > 15) brightness = 15;
display.setBright(brightness);
Serial.println("Bri = " + String(brightness));
}
// Один короткий клик — переключатель яркости
if (eb.hasClicks(1)) {
if (brightness >= 8) brightness = 2;
else brightness = 13;
display.setBright(brightness);
Serial.println("Bri = " + String(brightness));
}
// Двойной клик — включаем/выключаем мигающее двоеточие
if (eb.hasClicks(2)) {
blinking = !blinking;
dot = true; // чтоб при переключении сразу увидеть двоеточие
}
}
// ----- Вход/выход из режима настройки времени -----
if (eb.hold()) {
if (settingMode) {
// Уже в режиме настройки — значит, выходим из него и сохраняем новое время в RTC
// Сначала читаем текущую дату из RTC, чтобы год/месяц/день не сбрасывать:
Datime dtNow(watch);
// Меняем только часы/минуты (секунды при желании можно обнулить)
dtNow.hour = hourSetting;
dtNow.minute = minuteSetting;
dtNow.second = 0; // если нужно
if (watch.setTime(dtNow)) {
Serial.println("Время успешно установлено!");
} else {
Serial.println("Не удалось установить время!");
}
Serial.println("Выходим из режима настройки...");
settingMode = false;
} else {
// Не в режиме настройки — значит, входим в него: считываем из RTC текущее время
Datime dtNow(watch);
hourSetting = dtNow.hour;
minuteSetting = dtNow.minute;
Serial.println("Входим в режим настройки...");
settingMode = true;
settingStep = 0;
}
}
// ----- Режим настройки времени -----
if (settingMode) {
// Поворот энкодера влево
if (eb.left()) {
if (settingStep == 0) {
// Уменьшаем часы
hourSetting = (hourSetting + 23) % 24;
Serial.println("Изменение часов: " + String(hourSetting));
} else {
// Уменьшаем минуты
minuteSetting = (minuteSetting + 59) % 60;
Serial.println("Изменение минут: " + String(minuteSetting));
}
}
// Поворот энкодера вправо
if (eb.right()) {
if (settingStep == 0) {
// Увеличиваем часы
hourSetting = (hourSetting + 1) % 24;
Serial.println("Изменение часов: " + String(hourSetting));
} else {
// Увеличиваем минуты
minuteSetting = (minuteSetting + 1) % 60;
Serial.println("Изменение минут: " + String(minuteSetting));
}
}
// Короткий клик — переключение между настройкой часов и минут
if (eb.click()) {
settingStep = (settingStep + 1) % 2;
if (settingStep == 0) Serial.println("Переключились на редактирование ЧАСОВ");
else Serial.println("Переключились на редактирование МИНУТ");
}
// Отображение времени при настройке
display.clear();
display.setCursor(1, 1);
char timeString[6];
if (settingStep == 0) snprintf(timeString, sizeof(timeString), "%02d<%02d", hourSetting, minuteSetting);
else snprintf(timeString, sizeof(timeString), "%02d>%02d", hourSetting, minuteSetting);
display.print(timeString);
display.update();
} else {
// ----- Обычный режим отображения времени -----
if (watch.tick()) {
display.clear();
display.setCursor(1, 1);
Datime dt(watch);
// Мигающее двоеточие (если включено blinking)
if (blinking) dot = !dot;
// Формируем строку для дисплея
char timeString[6];
if (dot) snprintf(timeString, sizeof(timeString), "%02d:%02d", dt.hour, dt.minute);
else snprintf(timeString, sizeof(timeString), "%02d %02d", dt.hour, dt.minute);
display.print(timeString);
Serial.println(timeString);
display.update();
}
}
}