//код для esp-01s позволяюший при отсутсвии интернет соединения ,
// зайти на маску esp и поменять сеть wi-fi на нужную 
// для этого нужно подключиться к сети esp_ap и перейти по адресу 192.168.4.1




#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <U8g2lib.h>
#include <ArduinoOTA.h>
#include <time.h>
#include <EEPROM.h> // Добавлено для работы с EEPROM

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

// Определение пинов для светодиода и зуммера
//#define LED_PIN 2    // GPIO2, встроенный светодиод
#define BUZZER_PIN 0 // GPIO0

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

// Данные для подключения к Wi-Fi будут храниться в EEPROM
String ssid = "Beeline_2G_A7F18A";
String password = "iD2tAXJN3e";

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

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

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

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

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

// Переменные для управления миганием светодиода
unsigned long previousMillisLED = 0;
const long intervalLED = 1000; // Интервал мигания светодиода (1 секунда)
bool ledState = false;

// Глобальные переменные для управления будильником
bool alarmActive = false;
unsigned long alarmStartTime = 0;
const unsigned long alarmDuration = 10000; // Длительность будильника (10 секунд)

// Переменные для управления звуком будильника
unsigned long buzzerPreviousMillis = 0;
int buzzerState = 0;
const unsigned long beepOnDuration = 200;  // Длительность писка (200 мс)
const unsigned long beepOffDuration = 100; // Пауза между писками (100 мс)
const unsigned long pauseDuration = 1500;  // Пауза после двойного писка (1500 мс)

// Переменные для мигания времени и надписи "ALARM"
bool blinkState = false;
unsigned long previousMillisBlink = 0;
const unsigned long blinkInterval = 500; // Интервал мигания (мс)

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

// Максимальный размер буфера логов
const int maxLogSize = 2048;

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

// Прототипы функций
void logPrint(String message);
void logPrintln(String message);
void handleRoot();
void handleSetAlarm();
void handleTime();
void handleLog();
void checkAlarm();
void handleActiveAlarm();
void drawAlarmIcon(int x, int y);
void displayTime();
void startAPMode();
void handleRootAP();
void handleSaveWiFi();
void handleStatus();
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: #1E1E1E;
      color: #FFFFFF;
      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;
    }
    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: #FFFFFF;
      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;
    }
  </style>
</head>
<body>
  <h1>Будильник</h1>
  <div id="time">--:--:--</div>
  <div id="date">--.--.----</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] = dateTime.split(' ');
          document.getElementById('time').textContent = time;
          document.getElementById('date').textContent = date;
        })
        .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);

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

  // Настройка пинов
//  pinMode(LED_PIN, OUTPUT);
  //digitalWrite(LED_PIN, HIGH); // Светодиод выключен (активный LOW)
  pinMode(BUZZER_PIN, OUTPUT);
  digitalWrite(BUZZER_PIN, LOW); // Зуммер выключен

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

  // Чтение сохраненных 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 = 20000; // 20 секунд

    // Ожидание подключения
    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();

      // Настройка обработчиков веб-сервера
      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");

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

  if (apMode) {
    startAPMode();
  }
}

// Функция запуска режима точки доступа
void startAPMode() {
  WiFi.disconnect();
  WiFi.mode(WIFI_AP);
  WiFi.softAP("ESP_AP");
  
  IPAddress myIP = WiFi.softAPIP();
  Serial.print("Точка доступа запущена. IP адрес: ");
  Serial.println(myIP);
  
  // Настраиваем обработчики веб-сервера
  server.on("/", handleRootAP);
  server.on("/saveWiFi", handleSaveWiFi);
  server.on("/status", handleStatus);
  
  server.begin();
  logPrintln("HTTP сервер запущен.");
}

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

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

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

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

    // Обработка будильника или отображение времени
    if (alarmActive) {
      handleActiveAlarm();
    } else {
      displayTime();
    }

    // Мигание светодиода
    unsigned long currentMillis = millis();
    if (currentMillis - previousMillisLED >= intervalLED) {
      previousMillisLED = currentMillis;
      ledState = !ledState;
//      digitalWrite(LED_PIN, ledState ? LOW : HIGH);
    }
  } else {
    // В режиме AP можно реализовать другую логику, если требуется
    // Например, отображать на дисплее информацию о точке доступа
    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) {
  // Получаем текущее время (если подключены к Wi-Fi)
  String timeStr = "";
  if (!apMode && timeClient.isTimeSet()) {
    time_t rawTime = timeClient.getEpochTime();
    struct tm* timeInfo = localtime(&rawTime);
    char timeArray[9];
    strftime(timeArray, sizeof(timeArray), "%H:%M:%S", timeInfo);
    timeStr = String(timeArray);
  } else {
    timeStr = "--:--:--";
  }

  String logMessage = timeStr + " - " + message;

  Serial.print(logMessage);
  logBuffer += logMessage;

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

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

// Функция отображения времени и даты
void displayTime() {
  // Получение текущего времени
  time_t rawTime = timeClient.getEpochTime();
  struct tm* timeInfo = localtime(&rawTime);

  // Форматирование времени и даты
  char timeString[9];  // "HH:MM:SS"
  char dateString[11]; // "DD.MM.YYYY"
  strftime(timeString, sizeof(timeString), "%H:%M:%S", timeInfo);
  strftime(dateString, sizeof(dateString), "%d.%m.%Y", timeInfo);

  u8g2.firstPage();
  do {
    // Шрифт для времени
    u8g2.setFont(u8g2_font_fub14_tr);
    int16_t timeWidth = u8g2.getStrWidth(timeString);
    int16_t timeX = (128 - timeWidth) / 2;
    u8g2.drawStr(timeX, 20, timeString);

    // Шрифт для даты
    u8g2.setFont(u8g2_font_helvR12_tr);
    int16_t dateWidth = u8g2.getStrWidth(dateString);
    int16_t dateX = (128 - dateWidth) / 2;
    u8g2.drawStr(dateX, 38, dateString);

    // Отображение будильника
    if (alarmTime != "") {
      char alarmTimeString[6];
      alarmTime.toCharArray(alarmTimeString, sizeof(alarmTimeString));
      u8g2.setFont(u8g2_font_helvR12_tr);
      int iconWidth = 12;
      int alarmTimeWidth = u8g2.getStrWidth(alarmTimeString);
      int totalWidth = iconWidth + 2 + alarmTimeWidth;
      int startX = (128 - totalWidth) / 2;
      int iconX = startX;
      int iconY = 52;
      drawAlarmIcon(iconX, iconY);
      u8g2.drawStr(iconX + iconWidth + 2, iconY, alarmTimeString);
    }
  } while (u8g2.nextPage());
}

// Функция рисования иконки будильника
void drawAlarmIcon(int x, int y) {
  u8g2.drawCircle(x + 6, y - 6, 6);
  u8g2.drawLine(x + 6, y - 6, x + 6, y - 9);
  u8g2.drawLine(x + 6, y - 6, x + 9, y - 6);
  u8g2.drawCircle(x + 2, y - 12, 2);
  u8g2.drawCircle(x + 10, y - 12, 2);
  u8g2.drawLine(x + 3, y, x + 9, y);
}

// Функция проверки будильника
void checkAlarm() {
  if (alarmTime != "" && !alarmActive) {
    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("Будильник сработал!");
      alarmActive = true;
      alarmStartTime = millis();
      alarmTime = ""; // Сбрасываем будильник после срабатывания
      buzzerState = 0;
      buzzerPreviousMillis = millis();
      blinkState = false;
      previousMillisBlink = millis();
    }
  }
}

// Функция обработки активного будильника
void handleActiveAlarm() {
  unsigned long currentMillis = millis();

  // Управление миганием
  if (currentMillis - previousMillisBlink >= blinkInterval) {
    previousMillisBlink = currentMillis;
    blinkState = !blinkState;
  }

  u8g2.firstPage();
  do {
    if (blinkState) {
      // Отображаем время и дату
      time_t rawTime = timeClient.getEpochTime();
      struct tm* timeInfo = localtime(&rawTime);

      char timeString[9];  // "HH:MM:SS"
      char dateString[11]; // "DD.MM.YYYY"
      strftime(timeString, sizeof(timeString), "%H:%M:%S", timeInfo);
      strftime(dateString, sizeof(dateString), "%d.%m.%Y", timeInfo);

      u8g2.setFont(u8g2_font_fub14_tr);
      int16_t timeWidth = u8g2.getStrWidth(timeString);
      int16_t timeX = (128 - timeWidth) / 2;
      u8g2.drawStr(timeX, 20, timeString);

      u8g2.setFont(u8g2_font_helvR12_tr);
      int16_t dateWidth = u8g2.getStrWidth(dateString);
      int16_t dateX = (128 - dateWidth) / 2;
      u8g2.drawStr(dateX, 38, dateString);
    } else {
      // Отображаем "ALARM"
      u8g2.setFont(u8g2_font_fub25_tr);
      const char* alarmText = "ALARM";
      int16_t textWidth = u8g2.getStrWidth(alarmText);
      int16_t textX = (128 - textWidth) / 2;
      int16_t textY = 36;
      u8g2.drawStr(textX, textY, alarmText);
    }
  } while (u8g2.nextPage());

  // Управление зуммером
  switch (buzzerState) {
    case 0:
      digitalWrite(BUZZER_PIN, HIGH);
      buzzerPreviousMillis = currentMillis;
      buzzerState = 1;
      break;
    case 1:
      if (currentMillis - buzzerPreviousMillis >= beepOnDuration) {
        digitalWrite(BUZZER_PIN, LOW);
        buzzerPreviousMillis = currentMillis;
        buzzerState = 2;
      }
      break;
    case 2:
      if (currentMillis - buzzerPreviousMillis >= beepOffDuration) {
        digitalWrite(BUZZER_PIN, HIGH);
        buzzerPreviousMillis = currentMillis;
        buzzerState = 3;
      }
      break;
    case 3:
      if (currentMillis - buzzerPreviousMillis >= beepOnDuration) {
        digitalWrite(BUZZER_PIN, LOW);
        buzzerPreviousMillis = currentMillis;
        buzzerState = 4;
      }
      break;
    case 4:
      if (currentMillis - buzzerPreviousMillis >= pauseDuration) {
        buzzerPreviousMillis = currentMillis;
        buzzerState = 0;
      }
      break;
    default:
      buzzerState = 0;
      break;
  }

  // Проверка длительности будильника
  if (currentMillis - alarmStartTime >= alarmDuration) {
    digitalWrite(BUZZER_PIN, LOW);
    alarmActive = false;
    buzzerState = 0;
    u8g2.clearDisplay();
  }
}

// Обработчики веб-сервера в режиме 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);

  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();
}

void handleStatus() {
  String html = "<html>\
  <head><title>Статус оборудования</title></head>\
  <body>\
  <h1>Состояние оборудования</h1>";

  if (!apMode && timeClient.isTimeSet()) {
    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);

    html += "<p>Текущее время: " + String(timeString) + " " + String(dateString) + "</p>";
  } else {
    html += "<p>Время недоступно</p>";
  }

  html += "</body></html>";
  server.send(200, "text/html; charset=utf-8", html);
}

// Функции для работы с 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]);
  }
}

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);
} добавь в этот код хранение последней загруженной прошивки в текстовом файле на сервере