/***************************************************************************************************
                      Таймер для засвета ФР. Скетч написан ИИ по заказу Cybervad
***************************************************************************************************/

#include <GyverTM1637.h>   // Библиотека для работы с дисплеем TM1637
#include <EEPROM.h>       // Для сохранения значений таймеров

// Настройка подключения дисплея
#define PIN_CLK 12
#define PIN_DIO 11
GyverTM1637 tm(PIN_CLK, PIN_DIO);

// Настройки кнопок и сигнала
const int pinStart = A0;      // Кнопка Старт
const int pinTimer1 = A3;     // Выбор Таймера 1
const int pinTimer2 = A2;     // Выбор Таймера 2
const int pinTimer3 = A1;     // Выбор Таймера 3
const int buzzerPin = A4;     // Пин пищалки

// Подключение энкодера
const int encoderPinA = 2;    // Фаза А энкодера
const int encoderPinB = 3;    // Фаза B энкодера

// Значение таймеров хранится здесь
unsigned long timerValues[] = {5 * 1000, 30 * 60 * 1000}; // Изначально установлены два таймера (5 секунд и 30 минут)
uint8_t currentTimerIndex = 0; // Индекс текущего выбранного таймера

// Вспомогательная структура для энкодера
struct EncoderData {
    volatile byte prevState = 0; // Предыдущее состояние энкодера
};
EncoderData edata;

void setup() {
    Serial.begin(9600);              // Для отладки
    
    // Инициализация дисплея
    tm.brightness(2);               // Установка яркости дисплея
    tm.clear();                     // Очистка экрана
    
    // Инициализация кнопок
    pinMode(pinStart, INPUT_PULLUP);
    pinMode(pinTimer1, INPUT_PULLUP);
    pinMode(pinTimer2, INPUT_PULLUP);
    pinMode(pinTimer3, INPUT_PULLUP);

    // Инициализация энкодера
    pinMode(encoderPinA, INPUT_PULLUP);
    pinMode(encoderPinB, INPUT_PULLUP);

    // Присоединяем прерывание для энкодера
    attachInterrupt(digitalPinToInterrupt(encoderPinA), ISR_EncoderA, CHANGE);
    attachInterrupt(digitalPinToInterrupt(encoderPinB), ISR_EncoderB, CHANGE);

    // Читаем сохраненные значения таймеров из памяти
    for(int i = 0; i < sizeof(timerValues)/sizeof(unsigned long); ++i){
        unsigned long value = EEPROM.read(i*4 + 0) << 24 | 
                              EEPROM.read(i*4 + 1) << 16 |
                              EEPROM.read(i*4 + 2) << 8  |
                              EEPROM.read(i*4 + 3);
        if(value > 0 && value <= 30*60*1000){  
            timerValues[i] = value; // Сохраняем значение, если оно валидное
        }
    }

    Serial.println("Устройство готово!");
}

void loop() {
    checkButtons();
    delay(10);                     // Маленькая задержка между циклами
}

// Проверяет состояние кнопок и обрабатывает события
void checkButtons(){
    static bool lastStateStart = true;
    static bool lastStateTimer1 = true;
    static bool lastStateTimer2 = true;
    static bool lastStateTimer3 = true;

    bool startPressed = !digitalRead(pinStart);                 // Кнопка старта
    bool timer1Pressed = !digitalRead(pinTimer1);               // Выбор таймера 1
    bool timer2Pressed = !digitalRead(pinTimer2);               // Выбор таймера 2
    bool timer3Pressed = !digitalRead(pinTimer3);               // Выбор таймера 3

    if(startPressed != lastStateStart){
        if(startPressed){
            runSelectedTimer();
        }
        lastStateStart = startPressed;
    }

    if(timer1Pressed != lastStateTimer1){
        if(timer1Pressed){
            selectTimer(0);
        }
        lastStateTimer1 = timer1Pressed;
    }

    if(timer2Pressed != lastStateTimer2){
        if(timer2Pressed){
            selectTimer(1);
        }
        lastStateTimer2 = timer2Pressed;
    }

    if(timer3Pressed != lastStateTimer3){
        if(timer3Pressed){
            selectTimer(2);
        }
        lastStateTimer3 = timer3Pressed;
    }
}

// Запуск текущего выбранного таймера
void runSelectedTimer(){
    unsigned long selectedTime = timerValues[currentTimerIndex]; // Берем текущее значение таймера
    unsigned long endTime = millis() + selectedTime;

    while(millis() < endTime){
        updateDisplay(selectedTime - (millis() - (endTime - selectedTime))); // Обновляем экран
        delay(100);                                                          // Задержка обновления
    }

    beepSignal();                                                            // Звуковые сигналы по окончании таймера
    tm.displayInt(0);                                                        // Останавливаемся на значении '0'
    Serial.println("Таймер завершил отсчёт.");
}

// Обработка энкодера (фазы A и B)
void ISR_EncoderA() {
    byte newState = (digitalRead(encoderPinA) << 1) | digitalRead(encoderPinB);
    if(edata.prevState == 0 && newState == 1) {
        adjustTimerValue(-1); // Минус шаг (против часовой стрелки)
    }
    edata.prevState = newState;
}

void ISR_EncoderB() {
    byte newState = (digitalRead(encoderPinA) << 1) | digitalRead(encoderPinB);
    if(edata.prevState == 0 && newState == 2) {
        adjustTimerValue(+1); // Плюс шаг (по часовой стрелке)
    }
    edata.prevState = newState;
}

// Изменение значения таймера на указанную величину
void adjustTimerValue(int step){
    unsigned long newValue = timerValues[currentTimerIndex] + step * 5000; // Шаг 5 секунд
    if(newValue >= 5000 && newValue <= 30*60*1000){                         // Ограничиваем диапазон
        timerValues[currentTimerIndex] = newValue;
        saveToEeprom();                                                      // Сохраняем новое значение в память
        updateDisplay(timerValues[currentTimerIndex]);                        // Обновляем дисплей новым значением
        Serial.print("Новый таймер: ");
        Serial.println((newValue / 1000));
    }
}

// Сохраняем новые значения таймеров в энергонезависимую память
void saveToEeprom(){
    for(int i=0; i<sizeof(timerValues)/sizeof(unsigned long); i++){
        uint32_t val = timerValues[i];
        EEPROM.write(i*4 + 0, val >> 24 & 0xFF);
        EEPROM.write(i*4 + 1, val >> 16 & 0xFF);
        EEPROM.write(i*4 + 2, val >> 8 & 0xFF);
        EEPROM.write(i*4 + 3, val & 0xFF);
    }
    Serial.println("Значения таймеров сохранены в EEPROM.");
}

// Устанавливаем индекс текущего таймера
void selectTimer(uint8_t index){
    currentTimerIndex = index % 3;
    updateDisplay(timerValues[currentTimerIndex]);
    Serial.print("Выбран таймер №");
    Serial.println(currentTimerIndex + 1);
}

// Обновление дисплея текущим временем таймера
void updateDisplay(long timeInMs){
    int secondsRemaining = timeInMs / 1000;
    tm.displayInt(secondsRemaining);
}

// Генерируем тройной звуковой сигнал по окончании таймера
void beepSignal(){
    tone(buzzerPin, 1000); // Частота 1000 Гц
    delay(200);           // Длительность звучания
    noTone(buzzerPin);    // Пауза
    delay(100);           // Короткая пауза перед следующим звуком
    tone(buzzerPin, 1000); // Повтор звука дважды ещё раз
    delay(200);
    noTone(buzzerPin);
    delay(100);
    tone(buzzerPin, 1000); // Третий звук
    delay(200);
    noTone(buzzerPin);
}
4-Digit Display