#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Wire.h>
#include <Adafruit_BMP085.h>

// Настройки Wi-Fi
const char* ssid = "Ваш_SSID";
const char* password = "Ваш_пароль";

// Настройки статического IP-адреса
IPAddress local_IP(192, 168, 1, 184);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress primaryDNS(8, 8, 8, 8);
IPAddress secondaryDNS(8, 8, 4, 4);

// Экземпляр Wire для дисплея
TwoWire WireDisplay = TwoWire();

// Настройки дисплея SSD1306
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

// Создание экземпляра дисплея, используя WireDisplay
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &WireDisplay, -1);

// Настройки NTP клиента (UTC+3 для московского времени)
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 3 * 3600, 60000);

// Настройки веб-сервера
ESP8266WebServer server(80);

// Переменные для будильника
int alarmHour = -1;
int alarmMinute = -1;
bool alarmSet = false;

// Переменные для индикации будильника
bool alarmActive = false;
unsigned long alarmStartTime = 0;
unsigned long alarmDuration = 30000; // Длительность будильника в мс
unsigned long animationInterval = 50; // Интервал обновления анимации

// Учетные данные для аутентификации
const char* www_username = "admin";
const char* www_password = "Ваш_пароль_для_веб";

// Экземпляр датчика BMP180
Adafruit_BMP085 bmp;

// Предыдущие значения для сравнения
String prevTimeString = "";
String prevTempString = "";
String prevPressureString = "";

// Позиции текста на дисплее
const int timeLabelY = 0;
const int timeStringY = 16;
const int tempStringY = 32;
const int pressureStringY = 48;

// Глобальные переменные для хранения данных
float temperature = 0.0;
float pressure = 0.0;
String currentTimeString = "";

// Переменные для анимации будильника
int animationX = SCREEN_WIDTH;
unsigned long lastAnimationUpdate = 0;

// Прототипы функций
void handleRoot();
void handleGetTime();
void handleSetTime();
void handleSetAlarm();
void triggerAlarmEvent(); // Объявление функции

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

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

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

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

void setup() {
  Serial.begin(115200);

  // Инициализация стандартного Wire на пинах SDA (GPIO4) и SCL (GPIO5) для датчика BMP180
  Wire.begin(4, 5); // SDA, SCL

  // Инициализация датчика BMP180
  if (!bmp.begin()) {
    Serial.println("Не удалось найти датчик BMP180!");
    while (1) {
      delay(1000);
    }
  } else {
    Serial.println("Датчик BMP180 инициализирован.");
  }

  // Инициализация WireDisplay на других пинах SDA и SCL
  WireDisplay.begin(14, 12); // SDA - GPIO14 (D5), SCL - GPIO12 (D6)

  // Инициализация дисплея SSD1306
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("Не удалось инициализировать дисплей SSD1306");
    for (;;);
  }

  display.clearDisplay();
  display.display();
  display.setTextWrap(false);

  // Настройка статического IP
  if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
    Serial.println("Ошибка установки статического IP");
  }

  // Подключение к Wi-Fi
  WiFi.begin(ssid, password);
  Serial.print("Подключение к Wi-Fi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println(" Подключено!");
  Serial.print("IP адрес: ");
  Serial.println(WiFi.localIP());

  // Запуск NTP клиента
  timeClient.begin();

  // Настройка веб-сервера с аутентификацией
  server.on("/", HTTP_GET, handleRootWithAuth);
  server.on("/getTime", HTTP_GET, handleGetTimeWithAuth);
  server.on("/setTime", HTTP_POST, handleSetTimeWithAuth);
  server.on("/setAlarm", HTTP_POST, handleSetAlarmWithAuth);

  server.begin();
  Serial.println("Веб-сервер запущен");

  // Отображение статичного элемента "Time:"
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor((SCREEN_WIDTH - 6 * 5) / 2, timeLabelY); // Центрируем текст "Time:"
  display.print("Time:");
  display.display();
}

void loop() {
  timeClient.update();
  server.handleClient();

  // Получение текущего времени
  int currentHour = timeClient.getHours();
  int currentMinute = timeClient.getMinutes();
  int currentSecond = timeClient.getSeconds();

  // Формирование строки времени
  char timeString[9];
  snprintf(timeString, sizeof(timeString), "%02d:%02d:%02d", currentHour, currentMinute, currentSecond);
  currentTimeString = String(timeString); // Сохраняем текущее время в глобальной переменной

  // Проверка будильника
  if (alarmSet && currentHour == alarmHour && currentMinute == alarmMinute) {
    triggerAlarmEvent(); // Вызов функции
  }

  // Чтение данных с датчика BMP180
  temperature = bmp.readTemperature();
  Serial.print("Прочитанная температура: ");
  Serial.println(temperature);

  float pressure_hPa = bmp.readPressure() / 100.0; // Давление в hPa
  pressure = pressure_hPa * 0.75006375541921;      // Преобразование давления в мм рт. ст.
  Serial.print("Прочитанное давление: ");
  Serial.println(pressure);

  // Проверка на корректность температуры
  if (isnan(temperature)) {
    Serial.println("Ошибка: Температура не является числом (NaN).");
    temperature = 0.0; // Устанавливаем значение по умолчанию
  }

  // Формирование строк для отображения
  char tempString[16];
  snprintf(tempString, sizeof(tempString), "T:%.1fC", temperature);
  Serial.print("Строка температуры для отображения: ");
  Serial.println(tempString);

  char pressureString[16];
  snprintf(pressureString, sizeof(pressureString), "P:%.0fmmHg", pressure);
  Serial.print("Строка давления для отображения: ");
  Serial.println(pressureString);

  // Обработка индикации будильника
  if (alarmActive) {
    unsigned long currentTimeMillis = millis();

    if (currentTimeMillis - alarmStartTime >= alarmDuration) {
      // Останавливаем будильник
      alarmActive = false;
      // Очистка экрана и перерисовка статичных элементов
      display.clearDisplay();
      display.setTextSize(1);
      display.setTextColor(SSD1306_WHITE);
      display.setCursor((SCREEN_WIDTH - 6 * 5) / 2, timeLabelY);
      display.print("Time:");
      display.display();
      prevTimeString = ""; // Принудительное обновление времени
    } else {
      // Обновляем анимацию
      if (currentTimeMillis - lastAnimationUpdate >= animationInterval) {
        lastAnimationUpdate = currentTimeMillis;

        // Очистка предыдущего кадра анимации
        display.clearDisplay();

        // Вычисляем ширину текста
        int16_t textWidth = 6 * 6 * 2; // 6 символов, ширина символа ~6 пикселей, размер 2
        // Перемещаем позицию текста
        animationX -= 4; // Сдвигаем влево на 4 пикселя

        // Сбрасываем позицию, если текст полностью вышел за экран
        if (animationX < -textWidth) {
          animationX = SCREEN_WIDTH;
        }

        // Рисуем анимированный текст
        display.setTextSize(2);
        display.setTextColor(SSD1306_WHITE);
        display.setCursor(animationX, (SCREEN_HEIGHT - 16) / 2);
        display.print("ALARM!");
        display.display();
      }

      // Небольшая задержка
      delay(10);
      return; // Пропускаем остальную часть цикла, пока будильник активен
    }
  } else {
    // Сбрасываем позицию анимации, когда будильник не активен
    animationX = SCREEN_WIDTH;
  }

  // Обновление времени на дисплее
  if (prevTimeString != currentTimeString) {
    display.setTextSize(2);
    display.setTextColor(SSD1306_WHITE);
    display.fillRect(0, timeStringY, SCREEN_WIDTH, 16, SSD1306_BLACK); // Очистка области времени
    display.setCursor((SCREEN_WIDTH - currentTimeString.length() * 12) / 2, timeStringY);
    display.print(currentTimeString);
    display.display();
    prevTimeString = currentTimeString;
  }

  // Обновление температуры на дисплее
  if (prevTempString != String(tempString)) {
    Serial.print("Обновление температуры на дисплее: ");
    Serial.println(tempString);

    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);
    display.fillRect(0, tempStringY, SCREEN_WIDTH, 8, SSD1306_BLACK); // Очистка области температуры
    display.setCursor((SCREEN_WIDTH - strlen(tempString) * 6) / 2, tempStringY);
    display.print(tempString);
    display.display();
    prevTempString = String(tempString);
  }

  // Обновление давления на дисплее
  if (prevPressureString != String(pressureString)) {
    Serial.print("Обновление давления на дисплее: ");
    Serial.println(pressureString);

    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);
    display.fillRect(0, pressureStringY, SCREEN_WIDTH, 8, SSD1306_BLACK);
    display.setCursor((SCREEN_WIDTH - strlen(pressureString) * 6) / 2, pressureStringY);
    display.print(pressureString);
    display.display();
    prevPressureString = String(pressureString);
  }

  delay(200); // Задержка для плавного обновления
} ошибка Arduino: 1.8.19 (Mac OS X), Плата:"Generic ESP8266 Module, 80 MHz, Flash, Disabled (new aborts on oom), Disabled, All SSL ciphers (most compatible), 32KB cache + 32KB IRAM (balanced), Use pgm_read macros for IRAM/PROGMEM, dtr (aka nodemcu), 26 MHz, 40MHz, DOUT (compatible), 1MB (FS:64KB OTA:~470KB), 2, nonos-sdk 2.2.1+100 (190703), v2 Lower Memory, Disabled, None, Only Sketch, 115200"











/Users/pavelmakov/Library/Arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/3.1.0-gcc10.3-e5f9fec/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: sketch/ONLINE_CLOCK_WEB_TFT.ino.cpp.o: in function `_ZN6StringD4Ev':
/Users/pavelmakov/Library/Arduino15/packages/esp8266/hardware/esp8266/3.1.2/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h:226: undefined reference to `_Z13handleGetTimev'
/Users/pavelmakov/Library/Arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/3.1.0-gcc10.3-e5f9fec/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: sketch/ONLINE_CLOCK_WEB_TFT.ino.cpp.o: in function `_Z21handleGetTimeWithAuthv':
/Users/pavelmakov/Library/Arduino15/packages/esp8266/hardware/esp8266/3.1.2/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h:226: undefined reference to `_Z13handleSetTimev'
/Users/pavelmakov/Library/Arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/3.1.0-gcc10.3-e5f9fec/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: sketch/ONLINE_CLOCK_WEB_TFT.ino.cpp.o: in function `_ZN6StringC4Ec':
/Users/pavelmakov/Library/Arduino15/packages/esp8266/hardware/esp8266/3.1.2/cores/esp8266/WString.h:62: undefined reference to `_Z14handleSetAlarmv'
/Users/pavelmakov/Library/Arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/3.1.0-gcc10.3-e5f9fec/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: sketch/ONLINE_CLOCK_WEB_TFT.ino.cpp.o: in function `_ZN6StringD4Ev':
/Users/pavelmakov/Library/Arduino15/packages/esp8266/hardware/esp8266/3.1.2/cores/esp8266/WString.h:115: undefined reference to `_Z10handleRootv'
/Users/pavelmakov/Library/Arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/3.1.0-gcc10.3-e5f9fec/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: sketch/ONLINE_CLOCK_WEB_TFT.ino.cpp.o: in function `_ZN16esp8266webserver24ESP8266WebServerTemplateI10WiFiServerE12handleClientEv':
/Users/pavelmakov/Library/Arduino15/packages/esp8266/hardware/esp8266/3.1.2/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h:399: undefined reference to `_Z17triggerAlarmEventv'
/Users/pavelmakov/Library/Arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/3.1.0-gcc10.3-e5f9fec/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld: sketch/ONLINE_CLOCK_WEB_TFT.ino.cpp.o:(.text.loop+0xef): undefined reference to `_Z17triggerAlarmEventv'
collect2: error: ld returned 1 exit status
Несколько библиотек найдено для "Adafruit_SSD1306.h"
 Используется: /Users/pavelmakov/Documents/Arduino/sketch_aug24a/libraries/Adafruit_SSD1306
Не используется: /Users/pavelmakov/Documents/Arduino/sketch_aug24a/libraries/Adafruit_SSD1306_Wemos_Mini_OLED
exit status 1
Ошибка компиляции для платы Generic ESP8266 Module.


Этот отчёт будет иметь больше информации с
включенной опцией Файл -> Настройки ->
"Показать подробный вывод во время компиляции"