#define EB_DEB_TIME 50 // таймаут гашения дребезга кнопки (кнопка) 30.10.25
#define EB_CLICK_TIME 200 // таймаут ожидания кликов (кнопка)
#define EB_HOLD_TIME 600 // таймаут удержания (кнопка)
#define EB_STEP_TIME 200 // таймаут импульсного удержания (кнопка)
#define EB_FAST_TIME 30 // таймаут быстрого поворота (энкодер)
#include "RTClib.h"
#include <EncButton.h>
#include <Thread.h>
#include "TM1637.h"
RTC_DS1307 rtc; // Для часов реального времени на DS1307
char daysOfTheWeek[][20] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
const byte LED_3 = 17;
const byte LED_2 = 4; //11
const byte LED_1 = 11; //4
const byte On_Off = 6;
const byte MENU = 7;
const byte Plus = 8;
const byte Minus = 9;
const byte VADJ_1 = 3; // 5
const byte VADJ_2 = 10;
const byte VADJ_3 = 5; //3
const byte On_Off_3 = 14;
const byte On_Off_2 = 15;
const byte On_Off_1 = 16;
const byte CLK = 13; // TM1637
const byte DIO = 12; // TM1637
const byte standard_ON_minutes = 0, standard_ON_hours = 15; // предустановленные часы включения
const byte standard_OFF_minutes = 0, standard_OFF_hours = 12; // предустановленные часы выключения
const byte standard_Dawn_minutes = 0, standard_Dawn_hours = 7, standard_Dawn_time = 60; // предустановленные часы рассвета
const byte standard_Sunset_minutes = 0, standard_Sunset_hours = 18, standard_Sunset_time = 60; // предустановленные часы заката
TM1637 tm1637(CLK, DIO); //Создаём объект класса TM1637, в качестве параметров передаём номера пинов подключения
Button bOn_Off(On_Off); //pinMode(On_Off, INPUT_PULLUP);
Button bMENU(MENU); //pinMode(MENU, INPUT_PULLUP); //menu
Button bPlus(Plus); //pinMode(Plus, INPUT_PULLUP);
Button bMinus(Minus); //pinMode(Minus, INPUT_PULLUP); //!!!-
VirtButton MENU_On_Off;
VirtButton MENU_Plus;
VirtButton MENU_Minus;
VirtButton Plus_Minus;
struct SPosition { //Счетчик положения
byte val; //положение в 1 режиме (выбор светильника 1 2 3)
union {
float fbright;
uint8_t fbytes[4];
} bright; //Яркость для каждого модуля 4 байта
};
SPosition Position[4];
byte ERROR = 0;
byte ON_hours = -1, OFF_hours = -1, Dawn_hours = -1, Sunset_hours = -1, new_hours = -1; //Часы включения и выключения для режимов 2 и 3, 4, 6
byte ON_minutes = -1, OFF_minutes = -1, Dawn_minutes = -1, Sunset_minutes = -1, new_minutes = -1; //Минуты включения и выключения для режимов 2 и 3, 4, 6
byte Dawn_time = -1, Sunset_time = -1; //Длительность рассвета и заката
byte Dawn_hours_time = -1, Sunset_hours_time = -1, Dawn_minutes_time = -1, Sunset_minutes_time = -1;
byte Dawn_day_time = -1, Sunset_day_time = -1;
byte PWR = 0;
byte mode = 1; //Переключение режимов программирования
unsigned long currentTime = 0; // переменная времени
unsigned long previousMillis = 0; // Хранит время последнего обновления // для плавного включения 1 светодиода
unsigned long animationMillis = 0; // анимации заката и рассвета во время
int bright = 0; // текущая яркость светодиода // для плавного включения 1 светодиода
boolean ledState = 0; // переменная состояния светодиода
Thread ledThread_1 = Thread(); // создаём поток управления LED_1
Thread ledThread_2 = Thread(); // создаём поток управления LED_2
Thread ledThread_3 = Thread(); // создаём поток управления LED_3
Thread ledThread_4 = Thread(); // создаём поток управления двоеточием
// Пример использования энергонезависимого ОЗУ на DS1307.
// Вы можете записать до 56 байт с адреса 0 по 55.
// Данные будут сохраняться, пока DS1307 питается от батареи.
/*void printnvram(uint8_t address) {
Serial.print("Address 0x");
Serial.print(address, HEX);
Serial.print(" = 0x");
Serial.println(rtc.readnvram(address), HEX);
}*/
void setup() {
Serial.begin(9600);
pinMode(LED_3, OUTPUT);
pinMode(LED_2, OUTPUT);
pinMode(LED_1, OUTPUT);
pinMode(VADJ_1, OUTPUT);
pinMode(VADJ_2, OUTPUT);
pinMode(VADJ_3, OUTPUT);
tm1637.init(); //Инициализация модуля
//Установка яркости горения сегментов
/*
BRIGHT_TYPICAL = 2 Средний
BRIGHT_DARKEST = 0 Тёмный
BRIGHTEST = 7 Яркий
*/
tm1637.set(BRIGHT_TYPICAL);
if (!rtc.begin()) {
Serial.println("Не удалось найти RTC");
Serial.flush();
while (1) delay(10);
}
if (!rtc.isrunning()) {
Serial.println("RTC НЕ работает, установим время!");
// Когда время необходимо установить на новом устройстве или после отключения питания,
// следующая строка устанавливает RTC на дату и время компиляции этого скетча
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// Эта строка устанавливает RTC с явной датой и временем
rtc.adjust(DateTime(2000, 1, 9, 0, 0, 0));
ON_minutes = standard_ON_minutes; ON_hours = standard_ON_hours; // предустановленные часы включения для первого запуска
OFF_minutes = standard_OFF_minutes; OFF_hours = standard_OFF_hours; // предустановленные часы выключения для первого запуска
Dawn_minutes = standard_Dawn_minutes; Dawn_hours = standard_Dawn_hours; Dawn_time = standard_Dawn_time; // предустановленные часы рассвета для первого запуска
Sunset_minutes = standard_Sunset_minutes; Sunset_hours = standard_Sunset_hours; Sunset_time = standard_Sunset_time; // предустановленные часы заката для первого запуска
ERROR = 1;
}
/* Печать старого содержимого RAM при запуске.
Serial.println("Текущие значения NVRAM:"); for (int i = 0; i < 26; ++i) printnvram(i);
// Запись нескольких байтов в энергонезависимое хранилище RAM.
// ПРИМЕЧАНИЕ: чтение и запись возможны только с адресов от 0 до 55 (т. е. 56-байтовые значения).
Serial.println("Запись значений NVRAM.");
// Пример записи по одному байту за раз:
rtc.writenvram(0, 0xFE);
rtc.writenvram(1, 0xED);
// Пример записи нескольких байтов:
uint8_t writeData[4] = { 0xBE, 0xEF, 0x01, 0x02 };
rtc.writenvram(2, writeData, 4);
// Чтение байтов из энергонезависимого ОЗУ.
erial.println("Чтение значений NVRAM:");
// Пример чтения по одному байту за раз.
Serial.println(rtc.readnvram(0), HEX);
erial.println(rtc.readnvram(1), HEX);
// Пример чтения нескольких байтов:
uint8_t readData[4] = { 0 };
rtc.readnvram(readData, 4, 2);
Serial.println(readData[0], HEX);
Serial.println(readData[1], HEX);
Serial.println(readData[2], HEX);
Serial.println(readData[3], HEX);
Position[0] = { rtc.readnvram(0), 0 };
Position[1] = { rtc.readnvram(1), 0 };
Position[2] = { rtc.readnvram(2), 0 };
Position[3] = { rtc.readnvram(3), 0 };
*/
//считать val с памяти часов
if (Position[0].val > 3 || ERROR == 1) Position[0].val = 0; //вспомнить на каком светодиоде были в 1 режиме
if (Position[1].val > 2 || ERROR == 1) Position[1].val = 0; //вспомнить был ли там включен on off (было ли питание для вадж)
if (Position[2].val > 2 || ERROR == 1) Position[2].val = 0; //вспомнить был ли там включен on off (было ли питание для вадж)
if (Position[3].val > 2 || ERROR == 1) Position[3].val = 0; //вспомнить был ли там включен on off (было ли питание для вадж)
//считать bright с памяти часов
rtc.readnvram(Position[0].bright.fbytes, 4, 4);
rtc.readnvram(Position[1].bright.fbytes, 4, 8);
rtc.readnvram(Position[2].bright.fbytes, 4, 12);
if (Position[0].bright.fbright > 255 || ERROR == 1) Position[0].bright.fbright = 0; //вспомнить какая яркость на вадж была
if (Position[1].bright.fbright > 255 || ERROR == 1) Position[0].bright.fbright = 0; //вспомнить какая яркость на вадж была
if (Position[2].bright.fbright > 255 || ERROR == 1) Position[0].bright.fbright = 0; //вспомнить какая яркость на вадж была
//считать часы и минуты, которые настроил пользователь
ON_hours = rtc.readnvram(16);
OFF_hours = rtc.readnvram(17);
ON_minutes = rtc.readnvram(18);
OFF_minutes = rtc.readnvram(19);
Dawn_hours = rtc.readnvram(20);
Dawn_minutes = rtc.readnvram(21);
Sunset_hours = rtc.readnvram(22);
Sunset_minutes = rtc.readnvram(23);
Dawn_time = rtc.readnvram(24);
Sunset_time = rtc.readnvram(25);
//если одинаковое время начала рассвета и закаата, то установить значения по умолчанию, иначе конфликт с установкой времени на 00 00
if ((Dawn_minutes == Sunset_minutes && Dawn_hours == Sunset_hours) || ERROR == 1) {
Dawn_minutes = standard_Dawn_minutes;
Dawn_hours = standard_Dawn_hours;
Dawn_time = standard_Dawn_time;
Sunset_minutes = standard_Sunset_minutes;
Sunset_hours = standard_Sunset_hours;
Sunset_time = standard_Sunset_time;
}
//если одинаковое время включения и выключения всех светодиодов, то везде 255, иначе конфликт с установкой времени на 00 00
if ((ON_minutes == OFF_minutes && ON_hours == OFF_hours) || ERROR == 1) {
ON_minutes = -1;
OFF_minutes = -1;
ON_hours = -1;
OFF_hours = -1;
}
//вывод считанных данных с часов при запуске
Serial.print("ON time: "); Serial.print(ON_hours); Serial.print(":"); Serial.println(ON_minutes);
Serial.print("OFF time: "); Serial.print(OFF_hours); Serial.print(":"); Serial.println(OFF_minutes);
Serial.print("Dawn time: "); Serial.print(Dawn_hours); Serial.print(":"); Serial.print(Dawn_minutes); Serial.print(" min "); Serial.println(Dawn_time);
Serial.print("Sunset time: "); Serial.print(Sunset_hours); Serial.print(":"); Serial.print(Sunset_minutes); Serial.print(" min "); Serial.println(Sunset_time);
if (Position[1].val) { //если было питание для вадж, дать его и установить яркость
analogWrite(On_Off_1, 255);
analogWrite(VADJ_1, Position[0].bright.fbright);
}
if (Position[2].val) { //если было питание для вадж, дать его и установить яркость
analogWrite(On_Off_2, 255);
analogWrite(VADJ_2, Position[1].bright.fbright);
}
if (Position[3].val) { //если было питание для вадж, дать его и установить яркость
analogWrite(On_Off_3, 255);
analogWrite(VADJ_3, Position[2].bright.fbright);
}
//вывести дату и время текущие при запуске
DateTime now = rtc.now();
Serial.print(now.day(), DEC); Serial.print('/'); Serial.print(now.month(), DEC); Serial.print('/'); Serial.print(now.year(), DEC);
Serial.print(" ("); Serial.print(now.dayOfTheWeek(), DEC); Serial.print(") ");
Serial.print(now.hour(), DEC); Serial.print(':'); Serial.print(now.minute(), DEC); Serial.print(':'); Serial.println(now.second(), DEC);
//объявляем фунуцию ledBlink(инвертировать состояние светодиода), для каждого потока ledThread свтодиода, говорим что она должна срабатывать каждые 500 мс
ledThread_1.onRun(ledBlink1); ledThread_1.setInterval(500); // задаём интервал срабатывания, мсек
ledThread_2.onRun(ledBlink2); ledThread_2.setInterval(500); // задаём интервал срабатывания, мсек
ledThread_3.onRun(ledBlink3); ledThread_3.setInterval(500); // задаём интервал срабатывания, мсек
ledThread_4.onRun(ledBlink4); ledThread_4.setInterval(500); // задаём интервал срабатывания, мсек
//Анимация при включении
digitalWrite(LED_1, 255); delay(50); digitalWrite(LED_1, 0);
digitalWrite(LED_2, 255); delay(50); digitalWrite(LED_2, 0);
digitalWrite(LED_3, 255); delay(50); digitalWrite(LED_3, 0);
//отчистим дисплей, если тчо зависло, можно приветственную надпись вывести
tm1637.clearDisplay();
//tm1637.displayStr((char *)"0N"); delay(300);
}
//изменение ярости обработка нажатия +- и минимум максимум (долгое удернажантие (максимум минимум)) возращает новую ярркость
float brightless(float bright_funk) {
bPlus.tick();
bMinus.tick();
MENU_Plus.tick(bMENU, bPlus);
MENU_Minus.tick(bMENU, bMinus);
if (bPlus.click()) {
bright_funk -= 25.5;
if (bright_funk <= 0 || isnan(bright_funk)) bright_funk = 0;
}
if (bMinus.click()) {
bright_funk += 25.5;
if (bright_funk >= 255) bright_funk = 255;
}
if (bPlus.hold() && !MENU_Plus.hold()) { bright_funk = 0; }
if (bMinus.hold() && !MENU_Minus.hold()) { bright_funk = 255; }
return bright_funk;
}
//изменение часов обработка нажатия +- возращает новое время
byte MODEhours(byte TIME_funk) {
bPlus.tick();
bMinus.tick();
byte TIME;
if (bPlus.click()) {
TIME_funk++;
if (TIME_funk >= 24) TIME_funk = 23;
}
if (bMinus.click()) {
TIME_funk--;
if (TIME_funk == 0 || TIME_funk > 24) TIME_funk = 0;
}
return TIME_funk;
}
// минуты во времени (после часов) изменение минут (для часов максимум 45 кратность 15)
byte MODEminutes(byte TIME_funk) {
bPlus.tick();
bMinus.tick();
if (bPlus.click()) {
TIME_funk += 15;
if (TIME_funk >= 45) TIME_funk = 45;
}
if (bMinus.click()) {
TIME_funk -= 15;
if (TIME_funk == 0 || TIME_funk > 45) TIME_funk = 0;
}
return TIME_funk;
}
//изменение минут (для длпродолжительности рассвета / закатамаксимум 255 кратность 15) обработка нажатия +- возращает новое время
byte MODEminutes_2(byte TIME_funk) {
bPlus.tick();
bMinus.tick();
if (bPlus.click()) {
TIME_funk += 15;
if (TIME_funk >= 120) TIME_funk = 120; //byte максимум 255 держит рассвет и закат не более двух часов
}
if (bMinus.click()) {
TIME_funk -= 15;
if (TIME_funk <= 0 || TIME_funk >= 120) TIME_funk = 0;
}
return TIME_funk;
}
//изменение минут (для текущего времени шаг 1) удержание по 2 секунды
byte MODEminutes_3(byte TIME_funk) {
bPlus.tick();
bMinus.tick();
if (bPlus.click()) {
TIME_funk += 1;
if (TIME_funk >= 60) TIME_funk = 59; //byte максимум 255 держит рассвет и закат не более двух часов
}
if (bPlus.step()) {
TIME_funk += 1;
if (TIME_funk >= 60) TIME_funk = 59; //byte максимум 255 держит рассвет и закат не более двух часов
}
if (bMinus.click()) {
TIME_funk -= 1;
if (TIME_funk <= 0 || TIME_funk >= 60) TIME_funk = 0;
}
if (bMinus.step()) {
TIME_funk -= 1;
if (TIME_funk <= 0 || TIME_funk >= 60) TIME_funk = 0;
}
return TIME_funk;
}
// обработка нажатия OnOff
byte OnOff(byte val0) {
bOn_Off.tick();
if (bOn_Off.click()) { // отпустили кнопку
val0 = ++val0;
if (val0 == 2) val0 = 0;
}
return val0;
}
//помигать указанным светодиодом с delay (чтобы юзер ниче не натыкал) максимум 23 раза
void timeline_hours(byte LED, byte line) {
if (line < 24) //нужное кол-во раз 300 ждать 200 гореть
for (byte A = line; A > 0; A--) {
delay(300);
analogWrite(LED, 255);
delay(200);
analogWrite(LED, 0);
}
else { //неверное колво горит 1 секунду
delay(300);
analogWrite(LED, 255);
delay(1000);
analogWrite(LED, 0);
}
}
//помигать указанным светодиодом с delay (чтобы юзер ниче не натыкал) максимум 17 раз (255 минут)
void timeline_minutes(byte LED, byte line) {
line = line / 15;
if (line <= 17) //нужное кол-во раз 300 ждать 200 гореть
for (byte A = line; A > 0; A--) {
delay(200);
analogWrite(LED, 255);
delay(200);
analogWrite(LED, 0);
}
else { //неверное колво горит 1 секунду (невозможно но на всякий)
delay(300);
analogWrite(LED, 255);
delay(1000);
analogWrite(LED, 0);
}
}
void loop() {
bMENU.tick();
bOn_Off.tick();
MENU_On_Off.tick(bMENU, bOn_Off);
MENU_Plus.tick(bMENU, bPlus);
MENU_Minus.tick(bMENU, bMinus);
Plus_Minus.tick(bPlus, bMinus);
DateTime now = rtc.now();
//год месяц день часы минуты секунды на рассвет закат
DateTime Dawn_DateTime(0, 0, 0, Dawn_hours, Dawn_minutes, 0), Sunset_DateTime(0, 0, 0, Sunset_hours, Sunset_minutes, 0);
TimeSpan future_Dawn(Dawn_time * 60), future_Sunset(Sunset_time * 60); //TimeSpan хранится в секундах, потому *60, а так это продолжительность
float Dawn_brightless = 0, Sunset_brightless = 255;
if (mode == 1) {
tm1637.point(true);
//текущее время для часов
int8_t new_values[] = {now.hour()/10, now.hour()%10, now.minute()/10, now.minute()%10};
//Выводим массива на дисплей порязрядно
//tm1637.point(true); //Задание на включение разделителя
tm1637.display(0, new_values[0]); tm1637.display(1, new_values[1]); tm1637.display(2, new_values[2]); tm1637.display(3, new_values[3]);
}
/* //фикс ошибок, а зачем?
if (Dawn_minutes < 60 && Dawn_hours < 25) {
Dawn_minutes_time = Dawn_minutes + (Dawn_time % 60);
if (Dawn_minutes_time > 59) {
Dawn_hours_time = 1 + Dawn_time / 60 + Dawn_hours;
Dawn_minutes_time %= 60;
} else Dawn_hours_time = Dawn_time / 60 + Dawn_hours;
}
DateTime future_Dawn(now + TimeSpan(0, Dawn_hours_time, Dawn_minutes_time, 0));
if (Sunset_minutes < 60 && Sunset_hours < 25) {
Sunset_minutes_time = Sunset_minutes + (Sunset_time % 60);
if (Sunset_minutes_time > 59) {
Sunset_hours_time = 1 + Sunset_time / 60 + Sunset_hours;
Sunset_minutes_time %= 60;
} else Sunset_hours_time = Sunset_time / 60 + Sunset_hours;
}
DateTime future_Sunset(now + TimeSpan(0, Sunset_hours_time, Sunset_minutes_time, 0));
*/
/* //костыль кривой, при ребуте все равно врубает свет
if ((now.hour() == Dawn_DateTime.hour() && now.minute() == Dawn_DateTime.minute() && now.second() == 0) // вырубить светодиоды в первую секунду рассвета/заката
|| (now.hour() == Sunset_DateTime.hour() && now.minute() == Sunset_DateTime.minute() && now.second() == 0)) {
analogWrite(LED_1, 0);
analogWrite(LED_2, 0);
analogWrite(LED_3, 0);
}
*/
//Рассвет
if (now.hour() * 60 + now.minute() >= (Dawn_DateTime.hour() * 60 + Dawn_DateTime.minute())
&& (now.hour() * 60 + now.minute()) <= ((Dawn_DateTime.hour() + future_Dawn.hours()) * 60 + (Dawn_DateTime.minute() + future_Dawn.minutes()))
&& mode == 1) { // Dawn
//включаем запоминаем что включен on off (питание для вадж)
Position[1].val = 1; analogWrite(On_Off_1, 255);
Position[2].val = 1; analogWrite(On_Off_2, 255);
Position[3].val = 1; analogWrite(On_Off_3, 255);
//расчет текущей яркости 255 - (255/длительность)*(время сейчас(в мин) - время в начале рассвета(в мин))
Dawn_brightless = 255 - (255.0 / Dawn_time) * ((now.hour() * 60.0 + now.minute()) - (Dawn_DateTime.hour() * 60.0 + Dawn_DateTime.minute())) * 1.0;
Serial.print("Рассвет: "); Serial.println(Dawn_brightless); //вывести расчет текущей яркости
//включаем запоминаем что яркость вадж
if (Dawn_brightless < 256 && Dawn_brightless >= 0) {
//animation_dawn_sunset(0); //!!!
Position[0].bright.fbright = Dawn_brightless;
analogWrite(VADJ_1, Position[0].bright.fbright);
Position[1].bright.fbright = Dawn_brightless;
analogWrite(VADJ_2, Position[1].bright.fbright);
Position[2].bright.fbright = Dawn_brightless;
analogWrite(VADJ_3, Position[2].bright.fbright);
}
}
//Врубить все после рассвета ИЛИ если время включения
if ((((now.hour() * 60 + now.minute() - 1) == ((Dawn_DateTime.hour() + future_Dawn.hours()) * 60 + (Dawn_DateTime.minute() + future_Dawn.minutes())) && now.second() == 0)
|| (now.hour() == ON_hours && now.minute() == ON_minutes && now.second() == 0 && ON_hours != 255))
&& mode == 1) {
Position[1].val = 1;
Position[2].val = 1;
Position[3].val = 1;
analogWrite(On_Off_1, 255);
analogWrite(On_Off_2, 255);
analogWrite(On_Off_3, 255);
Sunset_brightless = 0;
Position[0].bright.fbright = Sunset_brightless;
analogWrite(VADJ_1, Position[0].bright.fbright);
Position[1].bright.fbright = Sunset_brightless;
analogWrite(VADJ_2, Position[1].bright.fbright);
Position[2].bright.fbright = Sunset_brightless;
analogWrite(VADJ_3, Position[2].bright.fbright);
}
//Закат
if (now.hour() * 60 + now.minute() >= (Sunset_DateTime.hour() * 60 + Sunset_DateTime.minute())
&& (now.hour() * 60 + now.minute()) <= ((Sunset_DateTime.hour() + future_Sunset.hours()) * 60 + (Sunset_DateTime.minute() + future_Sunset.minutes()))
&& mode == 1) { // Sunset
//включаем запоминаем что включен on off (питание для вадж)
Position[1].val = 1; analogWrite(On_Off_1, 255);
Position[2].val = 1; analogWrite(On_Off_2, 255);
Position[3].val = 1; analogWrite(On_Off_3, 255);
//расчет текущей яркости (255/длительность)*(время сейчас(в мин) - время в начале заката(в мин))
Sunset_brightless = (255.0 / Sunset_time) * ((now.hour() * 60.0 + now.minute()) - (Sunset_DateTime.hour() * 60.0 + Sunset_DateTime.minute())) * 1.0;
Serial.print("Закат: "); Serial.println(Sunset_brightless); //вывести расчет текущей яркости
//включаем запоминаем что яркость вадж
if (Sunset_brightless < 256 && Sunset_brightless >= 0) {
//animation_dawn_sunset(1);
Position[0].bright.fbright = Sunset_brightless;
analogWrite(VADJ_1, Position[0].bright.fbright);
Position[1].bright.fbright = Sunset_brightless;
analogWrite(VADJ_2, Position[1].bright.fbright);
Position[2].bright.fbright = Sunset_brightless;
analogWrite(VADJ_3, Position[2].bright.fbright);
}
}
//Все вырубить после заката ИЛИ если время выключения
if ((((now.hour() * 60 + now.minute() - 1) == ((Sunset_DateTime.hour() + future_Sunset.hours()) * 60 + (Sunset_DateTime.minute() + future_Sunset.minutes())) && now.second() == 0)
|| (now.hour() == OFF_hours && now.minute() == OFF_minutes && now.second() == 0 && OFF_hours != 255))
&& mode == 1) {
Position[1].val = 0;
Position[2].val = 0;
Position[3].val = 0;
analogWrite(On_Off_1, 0);
analogWrite(On_Off_2, 0);
analogWrite(On_Off_3, 0);
Sunset_brightless = 255;
Position[0].bright.fbright = Sunset_brightless;
analogWrite(VADJ_1, Position[0].bright.fbright);
Position[1].bright.fbright = Sunset_brightless;
analogWrite(VADJ_2, Position[1].bright.fbright);
Position[2].bright.fbright = Sunset_brightless;
analogWrite(VADJ_3, Position[2].bright.fbright);
}
//анимация во время заката или рассвета, нельзя сделать постепенное, потому что пины не аналоговые
if (millis() - animationMillis > 1000 & (Dawn_brightless != 0 || Sunset_brightless != 255)) {
animationMillis = millis();
bright -= 255;
if (bright < 0) bright = 255;
analogWrite(LED_1, bright);
analogWrite(LED_2, bright);
analogWrite(LED_3, bright);
}
// 1 режим, клик -- переход в первый режим
if (bMENU.hasClicks(1) & Dawn_brightless == 0 & Sunset_brightless == 255) {
mode = 1;
Position[0].val = ++Position[0].val;
if (Position[0].val >= 3) Position[0].val = 0;
//Serial.println(); //Serial.print("LED №"); //Serial.print(Position[0].val+1);
}
//2 режим нажать и удерживать кнопку Меню 3 сек – выбор времени включения всех драйверов (мигает 1св диод раз в секунду)
if (bMENU.holdFor(3000 - EB_HOLD_TIME)
&& bMENU.holdFor() < (6000 - EB_HOLD_TIME)
&& !MENU_On_Off.holdFor(3000 - EB_HOLD_TIME)
&& !MENU_Plus.holdFor(3000 - EB_HOLD_TIME)) {
Serial.println("2 Режим");
// игнорировать все события до отпускания кнопки
//skipEvents();
analogWrite(LED_2, 0);
analogWrite(LED_3, 0);
mode = 2;
}
//3 режим нажать и удерживать кнопку Меню 6 сек – выбор времени отключения всех драйверов (мигает 1св диод 2раза в секунду)
if (bMENU.holdFor(6000 - EB_HOLD_TIME)
&& !MENU_On_Off.holdFor(6000 - EB_HOLD_TIME)
&& !MENU_Plus.holdFor(6000 - EB_HOLD_TIME)) {
Serial.println("3 Режим");
analogWrite(LED_2, 0);
analogWrite(LED_3, 0);
mode = 3;
}
//4 режим нажать и удерживать кнопку Меню+ PWR 3 сек – выбор времени включения рассвет (мигает 2св диод раз в секунду)
if (MENU_On_Off.holdFor(3000 - EB_HOLD_TIME)
&& MENU_On_Off.holdFor() < (6000 - EB_HOLD_TIME)) {
Serial.println("4 Режим");
analogWrite(LED_1, 0);
analogWrite(LED_3, 0);
mode = 4;
}
//5 режим нажать и удерживать кнопку Меню+ PWR 6 сек – выбор длительности рассвета (мигает 2св диод 2раза в секунду)
if (MENU_On_Off.holdFor(6000 - EB_HOLD_TIME)) {
Serial.println("5 Режим");
analogWrite(LED_1, 0);
analogWrite(LED_3, 0);
mode = 5;
}
//6 режим нажать и удерживать кнопку Меню+ (+) 3 сек – выбор времени включения заката (мигает 3св диод раз в секунду)
if (MENU_Plus.holdFor(3000 - EB_HOLD_TIME)
&& MENU_Plus.holdFor() < (6000 - EB_HOLD_TIME)) {
Serial.println("6 Режим");
// bright =0;
analogWrite(LED_1, 0);
analogWrite(LED_2, 0);
mode = 6;
}
//7 режим нажать и удерживать кнопку Меню+ (+) 6 сек – выбор длительности заката (мигает 3св диод 2раза в секунду)
if (MENU_Plus.holdFor(6000 - EB_HOLD_TIME)) {
Serial.println("7 Режим");
// bright =0;
analogWrite(LED_1, 0);
analogWrite(LED_2, 0);
mode = 7;
}
//8 режим нажать и удерживать кнопку (+) + (-) 3 сек – установка ТЕКУЩЕГО ВРЕМЕНИ ВРУЧНУЮ
if (Plus_Minus.holdFor(3000 - EB_HOLD_TIME)
&& Plus_Minus.holdFor() < (6000 - EB_HOLD_TIME)) {
analogWrite(LED_1, 255);
analogWrite(LED_2, 255);
analogWrite(LED_2, 255);
new_hours = now.hour(); new_minutes = now.minute(); //записываем текущие часы и минуты, чтобы в будущем их установить
mode = 8;
}
//нажать и удерживать кнопку Меню+ (-) 3 сек – установка времени на 00.00, возвразение стандартных настроек, а потом перезагрузка скетча
if (MENU_Minus.holdFor(3000 - EB_HOLD_TIME)
&& MENU_Minus.holdFor() < (6000 - EB_HOLD_TIME)) {
//rtc.adjust(DateTime(2025, 5, 15, 20, 22, 0));
//сохранение всего, НА ВСЯКИЙ СЛУЧАЙ
rtc.writenvram(0, Position[0].val);
rtc.writenvram(1, Position[1].val);
rtc.writenvram(2, Position[2].val);
rtc.writenvram(3, Position[3].val);
rtc.writenvram(4, Position[0].bright.fbytes, 4);
rtc.writenvram(8, Position[1].bright.fbytes, 4);
rtc.writenvram(12, Position[2].bright.fbytes, 4);
rtc.writenvram(16, ON_hours);
rtc.writenvram(17, OFF_hours);
rtc.writenvram(18, ON_minutes);
rtc.writenvram(19, OFF_minutes);
rtc.writenvram(20, standard_Dawn_hours);
rtc.writenvram(21, standard_Dawn_minutes);
rtc.writenvram(22, standard_Sunset_hours);
rtc.writenvram(23, standard_Sunset_minutes);
rtc.writenvram(24, standard_Dawn_time);
rtc.writenvram(25, standard_Sunset_time);
rtc.adjust(DateTime(2000, 1, 9, 0, 0, 0));
delay(10);
void(* resetFunc) (void) = 0; // Reset MC function
resetFunc(); //вызов
}
switch (mode) {
case 1: //1 режим обрботка
// Если MENU 1 раз нажали то включить VADJ_1
if (Position[0].val == 0 & Dawn_brightless == 0 & Sunset_brightless == 255) {
Position[0].bright.fbright = brightless(Position[0].bright.fbright); //вспомнили яркость через brightless
analogWrite(LED_1, 255); //врубаем светодиод, остальные вырубаем
analogWrite(LED_2, 0);
analogWrite(LED_3, 0);
Position[1].val = OnOff(Position[1].val); //вспомнили должно ли быть питание через OnOff
if (Position[1].val) { //если есть, то дали питание и вывели яркость вадж
//Serial.print(" ON ");
analogWrite(On_Off_1, 255);
analogWrite(VADJ_1, Position[0].bright.fbright);
} else { //если нет, то вывели 0
//Serial.print(" OFF ");
analogWrite(On_Off_1, 0);
analogWrite(VADJ_1, 0);
}
//Serial.print(Position[0].bright.fbright);
}
// Если MENU 2 раз нажали то включить VADJ_2
if (Position[0].val == 1 & Dawn_brightless == 0 & Sunset_brightless == 255) {
Position[1].bright.fbright = brightless(Position[1].bright.fbright); //вспомнили яркость через brightless
analogWrite(LED_1, 0); //врубаем светодиод, остальные вырубаем
analogWrite(LED_2, 255);
analogWrite(LED_3, 0);
Position[2].val = OnOff(Position[2].val); //вспомнили должно ли быть питание через OnOff
if (Position[2].val) { //если есть, то дали питание и вывели яркость вадж
//Serial.print(" ON ");
analogWrite(On_Off_2, 255);
analogWrite(VADJ_2, Position[1].bright.fbright);
} else { //если нет, то вывели 0
//Serial.print(" OFF ");
analogWrite(On_Off_2, 0);
analogWrite(VADJ_2, 0);
}
//Serial.print(Position[1].bright.fbright);
}
// Если MENU 3 раз нажали то включить VADJ_3
if (Position[0].val == 2 & Dawn_brightless == 0 & Sunset_brightless == 255) {
Position[2].bright.fbright = brightless(Position[2].bright.fbright); //вспомнили яркость через brightless
analogWrite(LED_1, 0); //врубаем светодиод, остальные вырубаем
analogWrite(LED_2, 0);
analogWrite(LED_3, 255);
Position[3].val = OnOff(Position[3].val); //вспомнили должно ли быть питание через OnOff
if (Position[3].val) { //если есть, то дали питание и вывели яркость вадж
//Serial.print(" ON ");
analogWrite(On_Off_3, 255);
analogWrite(VADJ_3, Position[2].bright.fbright);
} else { //если нет, то вывели 0
//Serial.print(" OFF ");
analogWrite(On_Off_3, 0);
analogWrite(VADJ_3, 0);
}
//Serial.print(Position[2].bright.fbright);
}
break;
case 2: //2 режим обрботка
if (millis() - currentTime > 500) { // РАБОТАЕТ?
currentTime = millis();
ledState = !ledState; // меняем состояние светодиода на противоположное
digitalWrite(LED_1, ledState);
}
//вывод устанавливаемого времени на экранчик
if (ledThread_4.shouldRun()){
ledThread_4.run();
int8_t new_values_ON[] = {ON_hours/10, ON_hours%10, ON_minutes/10, ON_minutes%10};
tm1637.display(0, new_values_ON[0]); tm1637.display(1, new_values_ON[1]); tm1637.display(2, new_values_ON[2]); tm1637.display(3, new_values_ON[3]);
}
if (bOn_Off.hasClicks(1)) { //1 нажатие час мигает 2 св диод раз в сек
PWR++;
if (PWR > 1) PWR = 0;
}
switch (PWR) {
case 0:
if (ledThread_2.shouldRun()) {
analogWrite(LED_3, 0);
ledThread_2.run(); //режим мигания для настройки часов
}
ON_hours = MODEhours(ON_hours);
if (bMENU.click(2)) {
analogWrite(LED_1, 0);
analogWrite(LED_2, 0);
timeline_hours(LED_3, ON_hours);
}
if (bOn_Off.click(2)) ON_hours = -1;
break;
case 1:
if (PWR == 1 && ledThread_3.shouldRun()) {
analogWrite(LED_2, 0);
ledThread_3.run(); //режим мигания для настройки минут
}
ON_minutes = MODEminutes(ON_minutes);
if (bMENU.click(2)) {
analogWrite(LED_1, 0);
analogWrite(LED_3, 0);
timeline_minutes(LED_2, ON_minutes);
}
if (bOn_Off.click(2)) ON_minutes = -1;
break;
default:
PWR = 0;
break;
}
break;
case 3: //3 режим обрботка
if (millis() - currentTime > 250) {
currentTime = millis();
ledState = !ledState; // меняем состояние светодиода на противоположное
digitalWrite(LED_1, ledState);
}
//вывод устанавливаемого времени на экранчик
if (ledThread_4.shouldRun()){
ledThread_4.run();
int8_t new_values_OFF[] = {OFF_hours/10, OFF_hours%10, OFF_minutes/10, OFF_minutes%10};
tm1637.display(0, new_values_OFF[0]); tm1637.display(1, new_values_OFF[1]); tm1637.display(2, new_values_OFF[2]); tm1637.display(3, new_values_OFF[3]);
}
if (bOn_Off.hasClicks(1)) { //1 нажатие час мигает 2 св диод раз в сек
PWR++;
if (PWR > 1) PWR = 0;
}
switch (PWR) {
case 0:
if (ledThread_2.shouldRun()) {
analogWrite(LED_3, 0);
ledThread_2.run(); //режим мигания для настройки часов
}
OFF_hours = MODEhours(OFF_hours);
if (bMENU.click(2)) {
analogWrite(LED_1, 0);
analogWrite(LED_2, 0);
timeline_hours(LED_3, OFF_hours);
}
if (bOn_Off.click(2)) OFF_hours = -1;
break;
case 1:
if (PWR == 1 && ledThread_3.shouldRun()) {
analogWrite(LED_2, 0);
ledThread_3.run(); //режим мигания для настройки минут
}
OFF_minutes = MODEminutes(OFF_minutes);
if (bMENU.click(2)) {
analogWrite(LED_1, 0);
analogWrite(LED_3, 0);
timeline_minutes(LED_2, OFF_minutes);
}
if (bOn_Off.click(2)) OFF_minutes = -1;
break;
default:
PWR = 0;
break;
}
break;
case 4: //4 режим обрботка
//рассвет на LED_1 а че зачем?
if (millis() - previousMillis > 4) {
currentTime = millis();
bright += 4;
if (bright >= 255) bright = 0;
analogWrite(LED_1, bright);
}
//вывод устанавливаемого времени на экранчик
if (ledThread_4.shouldRun()){
ledThread_4.run();
int8_t new_values_Dawn[] = {Dawn_hours/10, Dawn_hours%10, Dawn_minutes/10, Dawn_minutes%10};
tm1637.display(0, new_values_Dawn[0]); tm1637.display(1, new_values_Dawn[1]); tm1637.display(2, new_values_Dawn[2]); tm1637.display(3, new_values_Dawn[3]);
}
//обработка нажатия On_Off
if (bOn_Off.hasClicks(1)) {
PWR++;
if (PWR > 1) PWR = 0;
}
//режим мигания для настройки часов/минут
switch (PWR) {
case 0:
if (ledThread_2.shouldRun()) { //запускаем мигание
analogWrite(LED_3, 0); //вырубаем 3 светодиод, видимо чтобы наверняка
ledThread_2.run(); //запускаем мигание 2
}
Dawn_hours = MODEhours(Dawn_hours); //запускаем настройку часов MODEhours
//если нажали 2 раза на MENU, то вывести кол-во часов timeline_hours
if (bMENU.click(2)) {
analogWrite(LED_1, 0);
analogWrite(LED_2, 0);
timeline_hours(LED_3, Dawn_hours);
}
//если нажали 2 раза на On_Off, то сбросить часы 255
if (bOn_Off.click(2)) Dawn_hours = -1;
break;
case 1:
if (ledThread_3.shouldRun()) { //запускаем мигание PWR == 1 &&
analogWrite(LED_2, 0); //вырубаем 2 светодиод, видимо чтобы наверняка
ledThread_3.run(); //запускаем мигание 3
//ledThread_4.run();
}
Dawn_minutes = MODEminutes(Dawn_minutes); //запускаем настройку минут MODEminutes
//если нажали 2 раза на MENU, то вывести кол-во минут timeline_minutes
if (bMENU.click(2)) {
analogWrite(LED_1, 0);
analogWrite(LED_3, 0);
timeline_minutes(LED_2, Dawn_minutes);
}
//если нажали 2 раза на On_Off, то сбросить минуты 255
if (bOn_Off.click(2)) Dawn_minutes = -1;
break;
default:
PWR = 0;
break;
}
break;
case 5: //5 режим обрботка
//быстрый рассвет на LED_1 чуть дольше а че зачем?
if (millis() - previousMillis > 4) {
currentTime = millis();
bright += 8;
if (bright >= 255) bright = 0;
analogWrite(LED_1, bright);
}
analogWrite(LED_2, 0);
analogWrite(LED_3, 0);
//вывод устанавливаемого времени на экранчик
if (ledThread_4.shouldRun()){
ledThread_4.run();
int8_t new_values_Dawn_time[] = {(Dawn_time/60)/10, (Dawn_time/60)%10, (Dawn_time%60)/10, (Dawn_time%60)%10};
tm1637.display(0, new_values_Dawn_time[0]); tm1637.display(1, new_values_Dawn_time[1]); tm1637.display(2, new_values_Dawn_time[2]); tm1637.display(3, new_values_Dawn_time[3]);
}
Dawn_time = MODEminutes_2(Dawn_time); //запускаем настройку минут MODEminutes
//если нажали 2 раза на MENU, то вывести кол-во минут timeline_minutes
if (bMENU.click(2)) {
analogWrite(LED_1, 0);
analogWrite(LED_3, 0);
timeline_minutes(LED_2, Dawn_time);
}
//если нажали 2 раза на On_Off, то сбросить минуты на 0
if (bOn_Off.click(2)) Dawn_time = 0;
break;
case 6: //6 режим обрботка
//закат на LED_1 а че зачем?
if (millis() - previousMillis > 4) {
currentTime = millis();
bright -= 4;
if (bright <= 0) bright = 255;
analogWrite(LED_1, bright);
}
//вывод устанавливаемого времени на экранчик
if (ledThread_4.shouldRun()){
ledThread_4.run();
int8_t new_values_Sunset[] = {Sunset_hours/10, Sunset_hours%10, Sunset_minutes/10, Sunset_minutes%10};
tm1637.display(0, new_values_Sunset[0]); tm1637.display(1, new_values_Sunset[1]); tm1637.display(2, new_values_Sunset[2]); tm1637.display(3, new_values_Sunset[3]);
}
//обработка нажатия On_Off
if (bOn_Off.click()) {
PWR++;
if (PWR > 1) PWR = 0;
}
//режим мигания для настройки часов/минут
switch (PWR) {
case 0:
if (ledThread_2.shouldRun()) { //запускаем миграние
analogWrite(LED_3, 0); //вырубаем 3 светодиод, видимо чтобы наверняка
ledThread_2.run(); //запускаем миграние 2
}
Sunset_hours = MODEhours(Sunset_hours); //запускаем настройку часов MODEhours
//если нажали 2 раза на MENU, то вывести кол-во часов timeline_hours
if (bMENU.click(2)) {
analogWrite(LED_1, 0);
analogWrite(LED_2, 0);
timeline_hours(LED_3, Sunset_hours);
}
//если нажали 2 раза на On_Off, то сбросить часы 255
if (bOn_Off.click(2)) Sunset_hours = -1;
break;
case 1:
if (ledThread_3.shouldRun()) {//PWR == 1 &&
analogWrite(LED_2, 0);
ledThread_3.run(); //режим мигания для минут
}
Sunset_minutes = MODEminutes(Sunset_minutes);
if (bMENU.click(2)) {
analogWrite(LED_1, 0);
analogWrite(LED_3, 0);
timeline_minutes(LED_2, Sunset_minutes);
}
if (bOn_Off.click(2)) Sunset_minutes = -1;
break;
//начинаем с часов всегда
default:
PWR = 0;
break;
}
break;
case 7: //нажать и удерживать кнопку Меню+ (+) 6 сек – выбор длительности заката (мигает 3св диод 2раза в секунду)
if (millis() - previousMillis > 4) {
currentTime = millis();
bright -= 8;
if (bright <= 0) bright = 255;
analogWrite(LED_1, bright);
}
analogWrite(LED_2, 0);
analogWrite(LED_3, 0);
//вывод устанавливаемого времени на экранчик
if (ledThread_4.shouldRun()){
ledThread_4.run();
int8_t new_values_Sunset_time[] = {(Sunset_time/60)/10, (Sunset_time/60)%10, (Sunset_time%60)/10, (Sunset_time%60)%10};
tm1637.display(0, new_values_Sunset_time[0]); tm1637.display(1, new_values_Sunset_time[1]); tm1637.display(2, new_values_Sunset_time[2]); tm1637.display(3, new_values_Sunset_time[3]);
}
Sunset_time = MODEminutes_2(Sunset_time);
if (bMENU.click(2)) {
analogWrite(LED_1, 0);
analogWrite(LED_3, 0);
timeline_minutes(LED_2, Sunset_time);
}
if (bOn_Off.click(2)) Sunset_time = -1;
break;
case 8: //нажать и удерживать кнопку (+) + (-) 3 сек – установка ТЕКУЩЕГО ВРЕМЕНИ ВРУЧНУЮ все светодиоды горят
//вывод устанавливаемого времени на экранчик
if (ledThread_4.shouldRun()){
ledThread_4.run();
int8_t new_values_time[] = {new_hours/10, new_hours%10, new_minutes/10, new_minutes%10};
tm1637.display(0, new_values_time[0]); tm1637.display(1, new_values_time[1]); tm1637.display(2, new_values_time[2]); tm1637.display(3, new_values_time[3]);
rtc.adjust(DateTime(2000, 1, 9, new_hours, new_minutes, 0));
}
//обработка нажатия On_Off
if (bOn_Off.click()) {
PWR++;
if (PWR > 1) PWR = 0;
}
//режим мигания для настройки часов/минут
switch (PWR) {
case 0:
if (ledThread_2.shouldRun()) { //запускаем миграние
analogWrite(LED_3, 0); //вырубаем 3 светодиод, видимо чтобы наверняка
ledThread_2.run(); //запускаем миграние 2
}
new_hours = MODEhours(new_hours); //запускаем настройку часов MODEhours
//если нажали 2 раза на MENU, то вывести кол-во часов timeline_hours
//if (bMENU.click(2)) {
// analogWrite(LED_1, 0);
// analogWrite(LED_2, 0);
// timeline_hours(LED_3, new_hours);
//}
//если нажали 2 раза на On_Off, то сбросить часы 255
//if (bOn_Off.click(2)) new_hours = -1;
break;
case 1:
if (ledThread_3.shouldRun()) {//PWR == 1 &&
analogWrite(LED_2, 0);
ledThread_3.run(); //режим мигания для минут
}
new_minutes = MODEminutes_3(new_minutes);
//if (bMENU.click(2)) {
// analogWrite(LED_1, 0);
// analogWrite(LED_3, 0);
// timeline_minutes(LED_2, new_minutes);
//}
//if (bOn_Off.click(2)) new_minutes = -1;
break;
//начинаем с часов всегда
default:
PWR = 0;
break;
}
break;
default:
mode = 1;
break;
}
rtc.writenvram(0, Position[0].val);
rtc.writenvram(1, Position[1].val);
rtc.writenvram(2, Position[2].val);
rtc.writenvram(3, Position[3].val);
rtc.writenvram(4, Position[0].bright.fbytes, 4);
rtc.writenvram(8, Position[1].bright.fbytes, 4);
rtc.writenvram(12, Position[2].bright.fbytes, 4);
rtc.writenvram(16, ON_hours);
rtc.writenvram(17, OFF_hours);
rtc.writenvram(18, ON_minutes);
rtc.writenvram(19, OFF_minutes);
rtc.writenvram(20, Dawn_hours);
rtc.writenvram(21, Dawn_minutes);
rtc.writenvram(22, Sunset_hours);
rtc.writenvram(23, Sunset_minutes);
rtc.writenvram(24, Dawn_time);
rtc.writenvram(25, Sunset_time);
delay(10);
}
//Хоть войд хоть не войд, НА КАЖДУЮ ЗАДАЧУ СВОЯ ОТДЕЛЬНАЯ ФУНКЦИЯ!!!
void ledBlink1() {
static bool ledStatus = false; // состояние светодиода Вкл/Выкл
ledStatus = !ledStatus; // инвертируем состояние
digitalWrite(LED_1, ledStatus); // включаем/выключаем светодиод
}
void ledBlink2() {
static bool ledStatus = false; // состояние светодиода Вкл/Выкл
ledStatus = !ledStatus; // инвертируем состояние
digitalWrite(LED_2, ledStatus); // включаем/выключаем светодиод
}
void ledBlink3() {
static bool ledStatus = false; // состояние светодиода Вкл/Выкл
ledStatus = !ledStatus; // инвертируем состояние
digitalWrite(LED_3, ledStatus); // включаем/выключаем светодиод
}
void ledBlink4() {
static bool ledStatus = false; // состояние точек Вкл/Выкл
ledStatus = !ledStatus; // инвертируем состояние
tm1637.point(ledStatus); // включаем/выключаем точки
}