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

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32

// Wi-Fi settings
const char* ssid = "Beeline_2G_A7F18A"; 
const char* password = "iD2tAXJN3e"; 

// OTA password
const char* otaPassword = "35539900";

// Thinger.io credentials
#define THINGER_USERNAME "PavelMy"
#define THINGER_DEVICE_ID "ESPPROGECT1"
#define THINGER_DEVICE_CREDENTIAL "Asus35539900"

// Buzzer pin
#define BUZZER_PIN D5 // Replace D5 with the pin you're using

// Create an instance of the display
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// NTP client settings
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 10800, 60000); // Time offset in seconds (3 hours)

// Web server
ESP8266WebServer server(80);

// Thinger.io instance
ThingerESP8266 thing(THINGER_USERNAME, THINGER_DEVICE_ID, THINGER_DEVICE_CREDENTIAL);

// Variables
String alarmTime = "";
bool alarmSet = false;
bool alarmTriggered = false; // Flag to prevent re-triggering in the same minute

String receivedMessage = ""; // To store the message received from Thinger.io

// Variables for scrolling text
int textXPosition = SCREEN_WIDTH; // Starting X position
String scrollMessage = "";

// Variables to track text sizes
int timeTextSize = 2;    // Text size for time
int otherTextSize = 1;   // Text size for alarm and messages

void handleRoot() {
    String html = 
    "<!DOCTYPE html>"
    "<html>"
    "<head>"
    "<meta name='viewport' content='width=device-width, initial-scale=1.0'/>"
    "<style>"
    "body { font-family: Arial, sans-serif; text-align: center; }"
    "h1 { font-size: 1.5em; }"
    "form { display: inline-block; margin-top: 20px; }"
    "input[type='number'] {"
    "  width: 60px;"
    "  height: 40px;"
    "  font-size: 1.5em;"
    "  text-align: center;"
    "  margin: 5px;"
    "}"
    "input[type='submit'] {"
    "  width: 100%;"
    "  padding: 10px;"
    "  font-size: 1.2em;"
    "}"
    "</style>"
    "</head>"
    "<body>"
    "<h1>Set Alarm</h1>"
    "<form action='/set_alarm' method='POST'>"
    "<div>"
    "Hour: <input type='number' name='hour' min='0' max='23' required>"
    "</div>"
    "<div>"
    "Minute: <input type='number' name='minute' min='0' max='59' required>"
    "</div>"
    "<input type='submit' value='Set Alarm'>"
    "</form>"
    "</body>"
    "</html>";
    server.send(200, "text/html", html);
}

void handleSetAlarm() {
    if (server.method() == HTTP_POST) {
        if (server.hasArg("hour") && server.hasArg("minute")) {
            String hour = server.arg("hour");
            String minute = server.arg("minute");

            // Format hour and minute to ensure two digits
            if (hour.length() < 2) hour = "0" + hour;
            if (minute.length() < 2) minute = "0" + minute;

            alarmTime = hour + ":" + minute;
            alarmSet = true;
            alarmTriggered = false; // Reset the trigger flag

            String html = 
            "<!DOCTYPE html>"
            "<html>"
            "<head>"
            "<meta name='viewport' content='width=device-width, initial-scale=1.0'/>"
            "<style>"
            "body { font-family: Arial, sans-serif; text-align: center; }"
            "h1 { font-size: 1.5em; }"
            "a { display: inline-block; margin-top: 20px; font-size: 1.2em; }"
            "</style>"
            "</head>"
            "<body>"
            "<h1>Alarm set for " + alarmTime + "</h1>"
            "<a href='/'>Back</a>"
            "</body>"
            "</html>";
            server.send(200, "text/html", html);

            Serial.print("Alarm set for: ");
            Serial.println(alarmTime);
        } else {
            server.send(400, "text/html", "Error: Hour and minute not provided.");
        }
    }
}

void setup() {
    Serial.begin(115200);
    display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
    display.clearDisplay();
    display.setTextWrap(false); // Disable auto line wrapping

    pinMode(BUZZER_PIN, OUTPUT);
    digitalWrite(BUZZER_PIN, LOW); // Turn off buzzer

    Serial.println("Connecting to Wi-Fi...");
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    Serial.println("\nConnected to Wi-Fi");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());

    timeClient.begin();
    ArduinoOTA.setPassword(otaPassword);
    ArduinoOTA.begin();

    server.on("/", HTTP_GET, handleRoot);
    server.on("/set_alarm", HTTP_POST, handleSetAlarm);
    server.begin();
    Serial.println("Web server started");

    thing.add_wifi(ssid, password);

    // Resource for receiving messages
    thing["display_message"] << [](pson& in) {
        if (in.is_empty()) {
            in = "Enter your message";
        } else if (in.is_string()) {
            receivedMessage = (const char*) in;  // Save the received message
            scrollMessage = receivedMessage;     // Set the message for scrolling
            textXPosition = SCREEN_WIDTH;        // Reset text position
            Serial.print("Received message: ");
            Serial.println(receivedMessage);
            playDoubleBeep(); // Play double beep when a new message is received
        }
    };

    // Resource for setting the alarm
    thing["set_alarm"] << [](pson& in) {
        if (in.is_empty()) {
            in["time"] = alarmTime; // Return the current alarm time
        } else {
            if (!in["time"].is_empty()) {
                alarmTime = (const char*) in["time"];
                alarmSet = true;
                alarmTriggered = false; // Reset the trigger flag
                Serial.print("Alarm set for: ");
                Serial.println(alarmTime);
            }
        }
    };
}

void loop() {
    ArduinoOTA.handle();
    server.handleClient();
    timeClient.update();
    thing.handle();  // Handle incoming messages

    String formattedTime = timeClient.getFormattedTime();

    // Alarm triggering check
    if (alarmSet && formattedTime.substring(0, 5) == alarmTime) {
        if (!alarmTriggered) {
            // Alarm goes off
            playAlarmBeep(); // Play the alarm buzzer
            alarmTriggered = true; // Set the flag to prevent re-triggering in the same minute
            // alarmSet = false; // Uncomment if you want the alarm to go off only once
        }
    } else {
        alarmTriggered = false; // Reset the flag if time has passed
    }

    display.clearDisplay();

    // Display current time
    display.setTextSize(timeTextSize); // Text size 2
    display.setFont(); // Use default font
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(0, 0); // Cursor position (X, Y)
    display.print(formattedTime); // Use print instead of println

    // Calculate text height for time
    int timeTextHeight = 8 * timeTextSize; // 8 pixels * text size (2)
    int yPosition = timeTextHeight;  // Start right after the time (Y = 16)

    // Display alarm time if set
    if (alarmSet) {
        display.setTextSize(otherTextSize); // Text size 1
        display.setCursor(0, yPosition);
        display.print("Alarm: ");
        display.print(alarmTime); // Use print instead of println
        // Update yPosition after display
        yPosition += 8 * otherTextSize; // 8 pixels * text size (1), Y increases by 8
    }

    // Display scrolling message
    if (scrollMessage != "") {
        display.setFont(); // Use default font
        display.setTextSize(otherTextSize); // Text size 1

        // Get text parameters
        int16_t x1, y1;
        uint16_t w, h;
        display.getTextBounds(scrollMessage, 0, yPosition, &x1, &y1, &w, &h);

        // Update text position
        textXPosition -= 2; // Scrolling speed

        // Reset position if text has moved off screen
        if (textXPosition < -w) {
            textXPosition = SCREEN_WIDTH;
        }

        display.setCursor(textXPosition, yPosition); // Set cursor at yPosition
        display.print(scrollMessage);
    }

    display.display(); // Update the display
    delay(30); // Delay for smooth scrolling
}

void playDoubleBeep() {
    // First beep
    digitalWrite(BUZZER_PIN, HIGH);
    delay(100); // Beep duration in milliseconds
    digitalWrite(BUZZER_PIN, LOW);
    delay(100); // Pause between beeps
    // Second beep
    digitalWrite(BUZZER_PIN, HIGH);
    delay(100); // Beep duration
    digitalWrite(BUZZER_PIN, LOW);
}

void playAlarmBeep() {
    // Play the alarm signal
    unsigned long alarmDuration = 5000; // Alarm duration in milliseconds
    unsigned long startTime = millis();

    while (millis() - startTime < alarmDuration) {
        digitalWrite(BUZZER_PIN, HIGH);
        delay(200); // Beep duration
        digitalWrite(BUZZER_PIN, LOW);
        delay(200); // Pause between beeps
        // Handle OTA and clients during the alarm
        ArduinoOTA.handle();
        server.handleClient();
        thing.handle();
    }
} добавь в код физическую кнопку для очистки экрана от полученного сообщения , а так же добавь подключение внешнего светодиода который будет мигать только когда приходит сообщение , после нажатия кнопки диод должен переставать мигать