Загрузка данных


// --- Библиотеки ---
#include <DHT.h>
#include <Servo.h> // Подключаем библиотеку для управления сервоприводом

// --- НАСТРОЙКИ ПИНОВ ---
#define DHTPIN 2              // Пин для датчика температуры DHT11
const int gasSensorPin = A1;  // Аналоговый вход для датчика газа KY-026
const int fanRelayPin = 6;    // Цифровой выход для реле вентилятора (KY-019)
const int gateLockRelayPin = 7;// Цифровой выход для реле блокировки ворот (мотор)
const int buzzerPin = 8;      // Цифровой выход для активного зуммера
const int gateSensorPin = 3;  // Цифровой пин для герконового датчика положения ворот
const int buttonGateControlPin = 4; // Кнопка для ручного управления воротами
const int servoPin = 9;       // Пин для подключения сервопривода

// --- КОНФИГУРАЦИЯ СИСТЕМЫ ---
#define DHTTYPE DHT11         // Тип датчика DHT
const int GAS_THRESHOLD = 2800;
const float TEMP_THRESHOLD_HIGH = 26.0;
const float TEMP_THRESHOLD_LOW = 25.0;
const unsigned long FAN_DELAY_MS = 10000;
const unsigned long BUZZER_ON_DURATION = 1000;
const unsigned long BUZZER_OFF_DURATION = 1000;
const unsigned long GATE_OPEN_TIME = 3000; // Время открытия ворот мотором (мс)

// --- НОВЫЕ КОНФИГУРАЦИИ ДЛЯ СЕРВОПРИВОДА ---
const int SERVO_POS_CLOSED = 0;     // Угол поворота для ЗАКРЫТОГО состояния
const int SERVO_POS_OPENED = 90;    // Угол поворота для ОТКРЫТОГО состояния

// --- СОЗДАНИЕ ОБЪЕКТОВ И ПЕРЕМЕННЫЕ СОСТОЯНИЯ ---
DHT dht(DHTPIN, DHTTYPE);
Servo emergencyDoorServo; // Создаем объект Servo
bool isDangerModeActive = false;
unsigned long ventilationEndTime = 0;
float currentTemperature = 0.0;
int currentGasValue = 0;

// Переменные для зуммера
bool isBuzzerActive = false;
bool isBuzzerOn = false;
unsigned long lastBuzzerChangeTime = 0;

// Переменные для ручного управления воротами
bool gateRelayState = false;
unsigned long gateStartTime = 0;

void setup() {
  Serial.begin(9600);
  delay(1000);
  Serial.println("=== ПОЛНАЯ СИСТЕМА БЕЗОПАСНОСТИ ===");

  pinMode(fanRelayPin, OUTPUT);
  pinMode(gateLockRelayPin, OUTPUT);
  pinMode(buzzerPin, OUTPUT);
  pinMode(gateSensorPin, INPUT_PULLUP);
  pinMode(buttonGateControlPin, INPUT_PULLUP); // Используем внутренний резистор

  digitalWrite(fanRelayPin, LOW);
  digitalWrite(gateLockRelayPin, HIGH); // Изначально ворота не активны
  digitalWrite(buzzerPin, LOW);

  dht.begin();

  // --- НАСТРОЙКА СЕРВОПРИВОДА ---
  emergencyDoorServo.attach(servoPin);
  emergencyDoorServo.write(SERVO_POS_CLOSED); // Стартовое положение - закрыто
}

void loop() {
  // 1. ЧТЕНИЕ ДАТЧИКОВ
  currentTemperature = dht.readTemperature();
  if (isnan(currentTemperature)) {
    Serial.println("Ошибка чтения данных с DHT11!");
    return;
  }
  currentGasValue = analogRead(gasSensorPin);

  // 2. ЛОГИКА ПЕРЕХОДОВ МЕЖДУ РЕЖИМАМИ (ГАЗ / ТЕМПЕРАТУРА)
  bool shouldActivateAlarm = (currentGasValue >= GAS_THRESHOLD) || (currentTemperature >= TEMP_THRESHOLD_HIGH);

  if (shouldActivateAlarm && !isDangerModeActive) {
    activateSafetyMeasures();
  } else if (!shouldActivateAlarm && isDangerModeActive) {
    deactivateSafetyMeasures();
    Serial.print("Угроза устранена. Вентиляция продолжит работу еще на ");
    Serial.print(FAN_DELAY_MS / 1000);
    Serial.println(" секунд.");
  }

  // 3. РУЧНОЕ УПРАВЛЕНИЕ ВОРОТАМИ (КНОПКА)
  handleManualGateControl();

  // 4. УПРАВЛЕНИЕ ВЕНТИЛЯТОРОМ ПО ТЕМПЕРАТУРЕ (вне режима опасности)
  bool tempControlFanOn = false;
  if (!isDangerModeActive) {
    bool fanState = digitalRead(fanRelayPin);
    if (!fanState && currentTemperature >= TEMP_THRESHOLD_HIGH) {
      tempControlFanOn = true;
      Serial.println("[АВТО] Вентилятор ВКЛЮЧЕН из-за высокой температуры.");
    } else if (fanState && currentTemperature <= TEMP_THRESHOLD_LOW) {
      tempControlFanOn = false;
      Serial.println("[АВТО] Вентилятор ВЫКЛЮЧЕН. Температура нормализовалась.");
    }
  }

  // 5. ФИНАЛЬНОЕ УПРАВЛЕНИЕ РЕЛЕ ВЕНТИЛЯТОРА
  bool shouldTurnFanOnNow = isDangerModeActive || tempControlFanOn;
  if (shouldTurnFanOnNow) {
    ventilationEndTime = millis() + FAN_DELAY_MS;
    Serial.print("Таймер вентиляции сброшен. Будет работать ");
    Serial.print(FAN_DELAY_MS / 1000);
    Serial.println(" сек.");
  }
  bool shouldFanBeOn = shouldTurnFanOnNow || (ventilationEndTime > millis());
  digitalWrite(fanRelayPin, shouldFanBeOn ? HIGH : LOW);

  // 6. УПРАВЛЕНИЕ ЗУММЕРОМ
  manageBuzzer();

  // 7. ВЫВОД СТАТУСА
  printSystemStatus();

  delay(2000);
}

// Функции активации/деактивации мер безопасности
void activateSafetyMeasures() {
  isDangerModeActive = true;
  ventilationEndTime = millis() + FAN_DELAY_MS;
  digitalWrite(gateLockRelayPin, HIGH); // Включаем реле мотора ворот
  emergencyDoorServo.write(SERVO_POS_OPENED); // Открываем механический засов
  isBuzzerActive = true;
  lastBuzzerChangeTime = millis();
  isBuzzerOn = true;
  digitalWrite(buzzerPin, HIGH);
  Serial.println("\n!!! ТРЕВОГА: Ворота открываются автоматически !!!");
}

void deactivateSafetyMeasures() {
  isDangerModeActive = false;
  digitalWrite(gateLockRelayPin, LOW); // Выключаем реле мотора ворот
  emergencyDoorServo.write(SERVO_POS_CLOSED); // Закрываем механический засов
  isBuzzerActive = false;
  digitalWrite(buzzerPin, LOW);
  Serial.println("Действие: Автоматическое открытие ворот завершено.");
}

// Функция управления прерывистым звуком зуммера
void manageBuzzer() {
  if (isBuzzerActive) {
    unsigned long currentMillis = millis();
    if ((isBuzzerOn && currentMillis - lastBuzzerChangeTime >= BUZZER_ON_DURATION) ||
        (!isBuzzerOn && currentMillis - lastBuzzerChangeTime >= BUZZER_OFF_DURATION)) {
      isBuzzerOn = !isBuzzerOn;
      digitalWrite(buzzerPin, isBuzzerOn ? HIGH : LOW);
      lastBuzzerChangeTime = currentMillis;
    }
  } else {
    digitalWrite(buzzerPin, LOW);
  }
}

// Функция ручного управления воротами (МОТОР + СЕРВО)
void handleManualGateControl() {
  static bool lastButtonState = HIGH;
  bool currentButtonState = digitalRead(buttonGateControlPin);

  // Проверяем нажатие кнопки (по спадающему фронту)
  if (lastButtonState == HIGH && currentButtonState == LOW) {
    if (!gateRelayState) {
      // Начинаем процесс открытия
      digitalWrite(gateLockRelayPin, HIGH); // Включаем реле мотора
      gateRelayState = true;
      gateStartTime = millis();
      Serial.println("Ворота: ОТКРЫТИЕ начато (кнопкой).");
    }
  }
  lastButtonState = currentButtonState;

  // Проверяем время работы мотора и останавливаем его
  if (gateRelayState && (millis() - gateStartTime >= GATE_OPEN_TIME)) {
    digitalWrite(gateLockRelayPin, LOW); // Останавливаем мотор
    gateRelayState = false;
    Serial.println("Ворота: СТОП (мотор выключен).");
  }

  // --- ЛОГИКА СЕРВОПРИВОДА ВРУЧНУЮ ---
  // Эта часть работает всегда, но имеет приоритет ниже автоматического режима
  // Если нет угрозы, серво управляется кнопкой напрямую
  if (!isDangerModeActive) {
    // Читаем кнопку снова для мгновенной реакции серво
    bool manualButtonPressed = (digitalRead(buttonGateControlPin) == LOW);
    if (manualButtonPressed) {
      emergencyDoorServo.write(SERVO_POS_OPENED);
    } else {
      emergencyDoorServo.write(SERVO_POS_CLOSED);
    }
  }
}

// Функция вывода статуса системы
void printSystemStatus() {
  static unsigned long lastPrintTime = 0;
  if (millis() - lastPrintTime >= 2000) {
    Serial.println("\n=== СТАТУС СИСТЕМЫ ===");

    Serial.print("Температура: ");
    Serial.print(currentTemperature);
    Serial.print(" °C | Авто-включение: ");
    Serial.print(TEMP_THRESHOLD_HIGH);
    Serial.print(" °C | Выключение: ");
    Serial.print(TEMP_THRESHOLD_LOW);
    Serial.println(" °C");

    Serial.print("Газ [RAW]: ");
    Serial.print(currentGasValue);
    Serial.print(" | Порог: ");
    Serial.println(GAS_THRESHOLD);

    Serial.print("Режим опасности: ");
    Serial.println(isDangerModeActive ? "АКТИВЕН" : "НЕТ");

    Serial.print("Сирена: ");
    Serial.println(isBuzzerActive ? "ПРОГРЕВАЕТ" : "ВЫКЛЮЧЕНА");

    Serial.print("Вентилятор: ");
    Serial.println(digitalRead(fanRelayPin) == HIGH ? "ВКЛЮЧЕН" : "ВЫКЛЮЧЕН");

    Serial.print("Мотор ворот: ");
    Serial.println(digitalRead(gateLockRelayPin) == HIGH ? "ВКЛЮЧЕН" : "ВЫКЛЮЧЕН");

    Serial.print("Серво-засов: ");
    Serial.println(emergencyDoorServo.read() == SERVO_POS_OPENED ? "ОТКРЫТ" : "ЗАКРЫТ");

    if (ventilationEndTime > millis()) {
      unsigned long remainingSec = (ventilationEndTime - millis()) / 1000;
      Serial.print("Таймер вентиляции: ");
      Serial.print(remainingSec);
      Serial.println(" сек осталось");
    }
    Serial.println("=======================");
    lastPrintTime = millis();
  }
}