#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <U8g2lib.h>
#include <ArduinoOTA.h>
#include <time.h>
#include <EEPROM.h>

// Определяем пины I2C для ESP-01S
#define I2C_SCL_PIN 0 // GPIO0 (SCL)
#define I2C_SDA_PIN 3 // GPIO3 (SDA)

// Определение пина для зуммера (если используется)
#define BUZZER_PIN 1 // GPIO1 (TXD)
// Примечание: использование GPIO1 для зуммера может мешать Serial Output

// Инициализация дисплея с использованием программного I2C
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(
  U8G2_R0,         // Поворот
  I2C_SCL_PIN,     // Пин SCL
  I2C_SDA_PIN,     // Пин SDA
  U8X8_PIN_NONE    // Пин сброса (не используется)
);

// Учетные данные для веб-сервера
const char* www_username = "admin";
const char* www_password = "35539900";

// Учетные данные для OTA
const char* otaHostname = "ESP01S_AlarmClock";
const char* otaPassword = "OTApassword123";

// Переменные для Wi-Fi
String ssid = "";
String password = "";

// Инициализация веб-сервера на порту 80
ESP8266WebServer server(80);

// Настройки NTP-клиента
const long utcOffsetInSeconds = 3 * 3600; // Московское время UTC+3
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds);

// Переменная для хранения времени будильника
String alarmTime = "";

// Буфер для хранения логов
String logBuffer = "";
const int maxLogSize = 2048;

// Переменная для контроля режима (STA или AP)
bool apMode = false;

// Переменные для мигающего индикатора на дисплее
unsigned long serverStartTime = 0;
unsigned long lastBlinkTime = 0;
const unsigned long blinkInterval = 100; // Интервал мигания в миллисекундах (быстрое мигание)
bool blinkState = false;

// Переменные для сохранения времени
unsigned long lastTimeSave = 0;
const unsigned long timeSaveInterval = 300000; // 5 минут
String lastShutdownTime = "";

// Адреса EEPROM для сохранения времени
const int lastTimeAddr = 200; // Адрес в EEPROM для сохранения последнего времени

// Прототипы функций
void logPrint(String message);
void logPrintln(String message);
void handleRoot();
void handleSetAlarm();
void handleTime();
void handleLog();
void checkAlarm();
void startAPMode();
void handleRootAP();
void handleSaveWiFi();
void writeStringToEEPROM(int addrOffset, const String& strToWrite);
String readStringFromEEPROM(int addrOffset);

// HTML-страница, хранимая в PROGMEM (обновлена для отображения последнего выключения)
const char htmlPage[] PROGMEM = R"=====(
<!DOCTYPE html>
<html>
<head>
  <title>Будильник</title>
  <style>
    body {
      background-color: #303030;
      color: #B2E0D9;
      font-family: Arial, sans-serif;
      text-align: center;
      padding-top: 20px;
    }
    h1 {
      color: #FF4081;
    }
    #time {
      font-size: 48px;
      margin: 20px 0;
    }
    #date {
      font-size: 32px;
      margin: 10px 0;
    }
    input[type=time] {
      padding: 10px;
      font-size: 18px;
      margin: 20px 0;
      border: 2px solid #B2E0D9;
      background-color: #424242;
      color: #B2E0D9;
    }
    input[type=submit] {
      background-color: #FF4081;
      color: #FFFFFF;
      border: none;
      padding: 15px 30px;
      font-size: 18px;
      cursor: pointer;
    }
    input[type=submit]:hover {
      background-color: #E91E63;
    }
    #log {
      background-color: #252526;
      color: #B2E0D9;
      text-align: left;
      padding: 10px;
      margin: 20px auto;
      width: 80%;
      height: 200px;
      overflow-y: scroll;
      font-family: monospace;
      font-size: 12px;
      border: 1px solid #333;
      white-space: pre-line;
    }
    #lastShutdown {
      margin: 20px 0;
      font-size: 16px;
    }
  </style>
</head>
<body>
  <h1>Будильник</h1>
  <div id="time">--:--:--</div>
  <div id="date">--.--.----</div>
  <div id="lastShutdown">Последнее выключение: загружается...</div>
  <form action="/setAlarm" method="POST">
    <label for="alarm">Время будильника:</label><br>
    <input type="time" id="alarm" name="alarm" required><br>
    <input type="submit" value="Установить будильник">
  </form>
  <div id="log">Загрузка логов...</div>
  <script>
    function updateTime() {
      fetch('/time')
        .then(response => response.text())
        .then(dateTime => {
          let [time, date, lastShutdown] = dateTime.split('|');
          document.getElementById('time').textContent = time;
          document.getElementById('date').textContent = date;
          document.getElementById('lastShutdown').textContent = 'Последнее выключение: ' + lastShutdown;
        })
        .catch(err => console.error(err));
    }
    setInterval(updateTime, 1000);
    updateTime();

    function updateLog() {
      fetch('/log')
        .then(response => response.text())
        .then(logData => {
          document.getElementById('log').textContent = logData;
          const logDiv = document.getElementById('log');
          logDiv.scrollTop = logDiv.scrollHeight;
        })
        .catch(err => console.error(err));
    }
    setInterval(updateLog, 1000);
    updateLog();
  </script>
</body>
</html>
)=====";

// Функция setup
void setup() {
  // Инициализация Serial Monitor
  Serial.begin(115200);
  // Примечание: Serial Monitor может не работать из-за переназначения пинов

  // Инициализация встроенного светодиода
  pinMode(LED_BUILTIN, OUTPUT);

  // Инициализация EEPROM
  EEPROM.begin(512);

  // Чтение сохраненного времени последнего выключения из EEPROM
  lastShutdownTime = readStringFromEEPROM(lastTimeAddr);
  if (lastShutdownTime == "") {
    lastShutdownTime = "Нет данных";
  }
  logPrintln("Последнее выключение: " + lastShutdownTime);

  // Мигание светодиода три раза при включении
  for (int i = 0; i < 3; i++) {
    digitalWrite(LED_BUILTIN, LOW);   // Включаем светодиод (активный низкий уровень)
    delay(500);
    digitalWrite(LED_BUILTIN, HIGH);  // Выключаем светодиод
    delay(500);
  }

  // Инициализация дисплея
  u8g2.begin();

  // Чтение сохраненных данных Wi-Fi из EEPROM
  ssid = readStringFromEEPROM(0);
  password = readStringFromEEPROM(100);

  // Проверка наличия сохраненных SSID и пароля
  if (ssid.length() == 0 || password.length() == 0) {
    logPrintln("Нет сохраненных данных Wi-Fi. Запуск режима точки доступа.");
    apMode = true;
  } else {
    // Подключение к Wi-Fi
    logPrint("Подключение к Wi-Fi...");
    WiFi.begin(ssid.c_str(), password.c_str());

    unsigned long startAttemptTime = millis();
    const unsigned long wifiConnectingTimeout = 60000; // 60 секунд

    // Ожидание подключения
    while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < wifiConnectingTimeout) {
      delay(500);
      logPrint(".");
    }

    if (WiFi.status() == WL_CONNECTED) {
      logPrintln("\nПодключено к Wi-Fi сети.");
      logPrint("IP адрес: ");
      logPrintln(WiFi.localIP().toString());

      // Запуск NTP-клиента
      timeClient.begin();
      timeClient.update(); // Получаем текущее время

      // Настройка обработчиков веб-сервера
      server.on("/", handleRoot);
      server.on("/setAlarm", HTTP_POST, handleSetAlarm);
      server.on("/time", handleTime);
      server.on("/log", handleLog);

      // Настройка OTA
      ArduinoOTA.setHostname(otaHostname);
      ArduinoOTA.setPassword(otaPassword);
      ArduinoOTA.begin();
      logPrintln("OTA настроен и готов.");

      // Запуск веб-сервера
      server.begin();
      logPrintln("Веб-сервер запущен на порту 80");

      // Запоминаем время запуска сервера для мигающего индикатора
      serverStartTime = millis();

      apMode = false;
    } else {
      logPrintln("\nНе удалось подключиться к Wi-Fi за 60 секунд. Запуск режима точки доступа.");
      apMode = true;
      startAPMode();
    }
  }

  if (apMode) {
    startAPMode();
  }

  // Инициализация времени последнего сохранения
  lastTimeSave = millis();
}

// Функция запуска режима точки доступа
void startAPMode() {
  WiFi.disconnect();
  WiFi.mode(WIFI_AP);
  WiFi.softAP("ESP_AP", www_password); // Установка пароля для точки доступа

  IPAddress myIP = WiFi.softAPIP();
  Serial.print("Точка доступа запущена. IP адрес: ");
  Serial.println(myIP);

  // Настраиваем обработчики веб-сервера
  server.on("/", handleRootAP);
  server.on("/saveWiFi", handleSaveWiFi);

  server.begin();
  logPrintln("HTTP сервер запущен.");
}

// Функция loop
void loop() {
  // Обработка запросов веб-сервера
  server.handleClient();

  if (!apMode) {
    // Обработка OTA
    ArduinoOTA.handle();

    // Обновление времени через NTP
    timeClient.update();

    // Проверка будильника
    checkAlarm();

    // Управление светодиодом для быстрого мигания в течение 5 секунд
    if (millis() - serverStartTime < 5000) {
      // Быстрое мигание светодиода
      if (millis() - lastBlinkTime >= blinkInterval) {
        lastBlinkTime = millis();
        blinkState = !blinkState;
        digitalWrite(LED_BUILTIN, blinkState ? LOW : HIGH); // Переключаем состояние светодиода
      }
    } else {
      // Выключаем светодиод после 5 секунд
      digitalWrite(LED_BUILTIN, HIGH); // Встроенный светодиод выключен при HIGH
    }

    // Отображение на дисплее
    u8g2.firstPage();
    do {
      u8g2.setFont(u8g2_font_helvR12_tr);

      // Отображаем время
      time_t rawTime = timeClient.getEpochTime();
      struct tm* timeInfo = localtime(&rawTime);
      char timeString[9]; // "HH:MM:SS"
      strftime(timeString, sizeof(timeString), "%H:%M:%S", timeInfo);
      u8g2.drawStr(0, 20, timeString);

      // Отображаем дату
      char dateString[11]; // "DD.MM.YYYY"
      strftime(dateString, sizeof(dateString), "%d.%m.%Y", timeInfo);
      u8g2.drawStr(0, 40, dateString);

      // Отображаем мигающий индикатор или будильник
      if (millis() - serverStartTime < 5000 && blinkState) {
        u8g2.drawStr(0, 60, "Сервер готов!");
      } else if (alarmTime != "") {
        String alarmText = "Будильник: " + alarmTime;
        u8g2.drawStr(0, 60, alarmText.c_str());
      }

    } while (u8g2.nextPage());

    // Сохранение времени в EEPROM каждые 5 минут
    if (millis() - lastTimeSave >= timeSaveInterval && timeClient.getEpochTime() > 0) {
      lastTimeSave = millis();

      time_t rawTime = timeClient.getEpochTime();
      struct tm* timeInfo = localtime(&rawTime);
      char timeString[20];
      strftime(timeString, sizeof(timeString), "%d.%m.%Y %H:%M:%S", timeInfo);
      String currentTimeString = String(timeString);

      // Сохраняем текущее время в EEPROM
      writeStringToEEPROM(lastTimeAddr, currentTimeString);
      EEPROM.commit();
      logPrintln("Время сохранено в EEPROM: " + currentTimeString);
    }

  } else {
    // В режиме точки доступа отображаем информацию
    u8g2.firstPage();
    do {
      u8g2.setFont(u8g2_font_helvR12_tr);
      u8g2.drawStr(0, 20, "Точка доступа");
      u8g2.drawStr(0, 40, "Активна");
      u8g2.drawStr(0, 60, "SSID: ESP_AP");
    } while (u8g2.nextPage());
  }
}

// Функции для логирования
void logPrint(String message) {
  Serial.print(message);
  logBuffer += message;

  // Ограничиваем размер буфера логов
  if (logBuffer.length() > maxLogSize) {
    logBuffer.remove(0, logBuffer.length() - maxLogSize);
  }
}

void logPrintln(String message) {
  logPrint(message + "\n");
}

// Функция проверки будильника
void checkAlarm() {
  if (alarmTime != "") {
    time_t rawTime = timeClient.getEpochTime();
    struct tm* timeInfo = localtime(&rawTime);
    char currentTime[6]; // "HH:MM"
    strftime(currentTime, sizeof(currentTime), "%H:%M", timeInfo);

    if (String(currentTime) == alarmTime) {
      logPrintln("Будильник сработал!");
      // Добавьте сюда код для активации зуммера или других действий
      // Например, включение зуммера:
      // pinMode(BUZZER_PIN, OUTPUT);
      // digitalWrite(BUZZER_PIN, HIGH); // Включаем зуммер
      // delay(1000); // Звучание 1 секунду
      // digitalWrite(BUZZER_PIN, LOW); // Выключаем зуммер

      alarmTime = ""; // Сбрасываем будильник после срабатывания
    }
  }
}

// Обработчики веб-сервера в режиме STA
void handleRoot() {
  if (!server.authenticate(www_username, www_password)) {
    return server.requestAuthentication();
  }
  String html((__FlashStringHelper*)htmlPage);
  server.send(200, "text/html; charset=utf-8", html);
}

void handleSetAlarm() {
  if (!server.authenticate(www_username, www_password)) {
    return server.requestAuthentication();
  }

  if (server.hasArg("alarm")) {
    alarmTime = server.arg("alarm");
    logPrintln("Будильник установлен на: " + alarmTime);
    server.sendHeader("Location", "/");
    server.send(303);
  } else {
    server.send(400, "text/plain; charset=utf-8", "Неверный запрос");
  }
}

void handleTime() {
  if (!server.authenticate(www_username, www_password)) {
    return server.requestAuthentication();
  }
  time_t rawTime = timeClient.getEpochTime();
  struct tm* timeInfo = localtime(&rawTime);

  char timeString[9];
  char dateString[11];

  strftime(timeString, sizeof(timeString), "%H:%M:%S", timeInfo);
  strftime(dateString, sizeof(dateString), "%d.%m.%Y", timeInfo);

  String dateTimeString = String(timeString) + "|" + String(dateString) + "|" + lastShutdownTime;

  server.send(200, "text/plain; charset=utf-8", dateTimeString);
}

void handleLog() {
  if (!server.authenticate(www_username, www_password)) {
    return server.requestAuthentication();
  }
  server.send(200, "text/plain; charset=utf-8", logBuffer);
}

// Обработчики веб-сервера в режиме AP
void handleRootAP() {
  String html = "<html>\
  <head><title>Настройка Wi-Fi</title></head>\
  <body>\
  <h1>Настройка Wi-Fi</h1>\
  <form action='/saveWiFi' method='post'>\
  SSID:<br><input type='text' name='ssid'><br>\
  Пароль:<br><input type='password' name='password'><br><br>\
  <input type='submit' value='Сохранить'>\
  </form>\
  </body>\
  </html>";
      
  server.send(200, "text/html; charset=utf-8", html);
}

void handleSaveWiFi() {
  String newSSID = server.arg("ssid");
  String newPassword = server.arg("password");

  // Сохраняем новые данные в EEPROM
  writeStringToEEPROM(0, newSSID);
  writeStringToEEPROM(100, newPassword);
  EEPROM.commit();

  String html = "<html><body><h1>Настройки сохранены. Перезагрузка устройства...</h1></body></html>";
  server.send(200, "text/html; charset=utf-8", html);

  delay(2000);
  ESP.restart();
}

// Функции для работы с EEPROM
void writeStringToEEPROM(int addrOffset, const String &strToWrite) {
  byte len = strToWrite.length();
  EEPROM.write(addrOffset, len);
  for (int i = 0; i < len; i++) {
    EEPROM.write(addrOffset + 1 + i, strToWrite[i]);
  }
  // Заполняем оставшееся пространство нулями
  for (int i = len; i < 31; i++) {
    EEPROM.write(addrOffset + 1 + i, 0);
  }
}

String readStringFromEEPROM(int addrOffset) {
  int len = EEPROM.read(addrOffset);
  if (len < 0 || len > 31) return ""; // Проверка на допустимую длину
  char data[len + 1];
  for (int i = 0; i < len; i++) {
    data[i] = EEPROM.read(addrOffset + 1 + i);
  }
  data[len] = '\0';
  return String(data);
}
адаптируй код для подключения к esp8266 вместо esp-01s и ра пиши подключение по выводам