// --- Определение пинов Arduino ---
// Мотор драйвер (TA6586)
const int motorIn1Pin = 12; // Пин для управления направлением 1 (например, открыть)
const int motorIn2Pin = 11; // Пин для управления направлением 2 (например, закрыть)
//const int motorEnablePin = 4; // Пин для включения/выключения мотора (ENA/ENB на TA6586)
// Датчики Холла (концевики)
const int doorOpenSensorPin = 3; // LOW, когда дверца полностью ОТКРЫТА
const int doorClosedSensorPin = 2; // LOW, когда дверца полностью ЗАКРЫТА
// Кнопки ручного управления
const int buttonOpenPin = 7; // Кнопка "Открыть", LOW при нажатии (с INPUT_PULLUP)
const int buttonClosePin = 6; // Кнопка "Закрыть", LOW при нажатии (с INPUT_PULLUP)
// Фоторезистор (LDR)
const int ldrPin = A7; // Аналоговый пин для чтения освещенности
// LED для индикации (например, встроенный на D13)
const int statusLedPin = 13;
// --- Настройки и пороги ---
// Пороги освещенности (значения A0 от 0 до 1023)
// Эти значения нужно будет откалибровать на месте!
// Чем выше значение, тем светлее.
const int dawnThreshold = 700; // Порог для рассвета (дверь открывается)
const int duskThreshold = 300; // Порог для сумерек (дверь закрывается)
// Важно: dawnThreshold должен быть больше duskThreshold для гистерезиса,
// чтобы избежать быстрого открытия/закрытия при пограничной освещенности.
const long motorTimeoutMs = 30000; // Максимальное время работы мотора в одну сторону (30 секунд)
// Если датчик не сработает за это время, мотор остановится.
const long debounceDelay = 50; // Задержка для подавления дребезга кнопок (мс)
// --- Переменные состояния ---
// Состояния дверцы
enum DoorState {
DOOR_OPEN, // Дверца полностью открыта
DOOR_CLOSED, // Дверца полностью закрыта
DOOR_OPENING, // Дверца открывается
DOOR_CLOSING, // Дверца закрывается
DOOR_STOPPED_MID // Дверца остановлена в промежуточном положении (например, после ручного управления)
};
DoorState currentDoorState = DOOR_STOPPED_MID; // Начальное состояние неизвестно, будет откалибровано
// Режим управления
enum ControlMode {
MODE_AUTOMATIC, // Автоматическое управление по освещенности
MODE_MANUAL // Ручное управление кнопками
};
ControlMode currentControlMode = MODE_AUTOMATIC; // Начинаем в автоматическом режиме
// Переменные для отслеживания времени (для таймаутов и дребезга)
unsigned long motorStartTime = 0;
unsigned long lastButtonOpenPressTime = 0;
unsigned long lastButtonClosePressTime = 0;
// --- Функции управления мотором ---
// Остановить мотор
void motorOff() {
//digitalWrite(motorEnablePin, LOW); // Отключить EN пин драйвера
digitalWrite(motorIn1Pin, LOW); // Установить оба пина направления в LOW
digitalWrite(motorIn2Pin, LOW); // для предотвращения нежелательного движения
Serial.println("Мотор остановлен.");
}
// Запустить мотор на открытие дверцы
void startOpeningDoor() {
if (currentDoorState == DOOR_OPENING || digitalRead(doorOpenSensorPin) == LOW) {
// Уже открывается или уже открыта
return;
}
motorStartTime = millis(); // Записываем время начала движения для таймаута
digitalWrite(motorIn1Pin, HIGH); // Установить направление 1 (проверьте, соответствует ли открытию)
digitalWrite(motorIn2Pin, LOW);
//digitalWrite(motorEnablePin, HIGH); // Включить EN пин драйвера
currentDoorState = DOOR_OPENING;
Serial.println("Дверца начинает ОТКРЫВАТЬСЯ...");
digitalWrite(statusLedPin, HIGH); // Включить LED во время движения
}
// Запустить мотор на закрытие дверцы
void startClosingDoor() {
if (currentDoorState == DOOR_CLOSING || digitalRead(doorClosedSensorPin) == LOW) {
// Уже закрывается или уже закрыта
return;
}
motorStartTime = millis(); // Записываем время начала движения для таймаута
digitalWrite(motorIn1Pin, LOW);
digitalWrite(motorIn2Pin, HIGH); // Установить направление 2 (проверьте, соответствует ли закрытию)
//digitalWrite(motorEnablePin, HIGH); // Включить EN пин драйвера
currentDoorState = DOOR_CLOSING;
Serial.println("Дверца начинает ЗАКРЫВАТЬСЯ...");
digitalWrite(statusLedPin, HIGH); // Включить LED во время движения
}
// --- Setup: Инициализация ---
void setup() {
Serial.begin(9600); // Инициализация последовательного порта для отладки
Serial.println("--- Контроллер дверцы курятника запущен ---");
// Установка режимов работы пинов
pinMode(motorIn1Pin, OUTPUT);
pinMode(motorIn2Pin, OUTPUT);
//pinMode(motorEnablePin, OUTPUT);
pinMode(doorOpenSensorPin, INPUT_PULLUP); // Датчики Холла (предполагаем активный HIGH)
pinMode(doorClosedSensorPin, INPUT_PULLUP); // Если нужны подтягивающие резисторы, используйте INPUT_PULLUP
pinMode(buttonOpenPin, INPUT_PULLUP); // Кнопки с внутренними подтягивающими резисторами
pinMode(buttonClosePin, INPUT_PULLUP); // (нажатие кнопки дает LOW)
pinMode(statusLedPin, OUTPUT);
// Изначально выключить мотор
motorOff();
// Калибровка при запуске: пытаемся закрыть дверцу, чтобы знать её начальное положение
Serial.println("Выполнение начальной калибровки: попытка закрыть дверцу...");
startClosingDoor();
unsigned long calibrationStartTime = millis();
while (digitalRead(doorClosedSensorPin) == HIGH && (millis() - calibrationStartTime < motorTimeoutMs)) {
// Ждем, пока дверца закроется или истечет таймаут
delay(50); // Небольшая задержка для предотвращения чрезмерного использования CPU
}
motorOff(); // Остановить мотор после калибровки
if (digitalRead(doorClosedSensorPin) == LOW) {
currentDoorState = DOOR_CLOSED;
Serial.println("Калибровка завершена: Дверца ЗАКРЫТА.");
} else {
currentDoorState = DOOR_STOPPED_MID; // Не удалось закрыть до конца
Serial.println("Калибровка НЕ завершена: Дверца не достигла ЗАКРЫТОГО состояния или таймаут.");
Serial.println("Пожалуйста, проверьте механику или концевики.");
}
currentControlMode = MODE_AUTOMATIC; // После калибровки переходим в автоматический режим
}
// --- Loop: Основной цикл программы ---
void loop() {
// --- Чтение текущих состояний датчиков ---
int ldrValue = analogRead(ldrPin);
bool isDoorOpenSensorActive = (digitalRead(doorOpenSensorPin) == LOW);
bool isDoorClosedSensorActive = (digitalRead(doorClosedSensorPin) == LOW);
// --- Обработка кнопок ручного управления (с подавлением дребезга) ---
bool buttonOpenIsPressed = !digitalRead(buttonOpenPin); // LOW при нажатии
bool buttonCloseIsPressed = !digitalRead(buttonClosePin); // LOW при нажатии
if (buttonOpenIsPressed && (millis() - lastButtonOpenPressTime > debounceDelay)) {
lastButtonOpenPressTime = millis();
Serial.println("Нажата кнопка: ОТКРЫТЬ");
currentControlMode = MODE_MANUAL; // Переходим в ручной режим
if (currentDoorState != DOOR_OPENING && !isDoorOpenSensorActive) {
startOpeningDoor();
} else if (currentDoorState == DOOR_STOPPED_MID) { // Если остановили в середине, можно продолжить
startOpeningDoor();
}
}
if (buttonCloseIsPressed && (millis() - lastButtonClosePressTime > debounceDelay)) {
lastButtonClosePressTime = millis();
Serial.println("Нажата кнопка: ЗАКРЫТЬ");
currentControlMode = MODE_MANUAL; // Переходим в ручной режим
if (currentDoorState != DOOR_CLOSING && !isDoorClosedSensorActive) {
startClosingDoor();
} else if (currentDoorState == DOOR_STOPPED_MID) { // Если остановили в середине, можно продолжить
startClosingDoor();
}
}
// --- Логика работы дверцы в зависимости от состояния ---
switch (currentDoorState) {
case DOOR_OPENING:
if (isDoorOpenSensorActive) {
motorOff();
currentDoorState = DOOR_OPEN;
Serial.println("Дверца полностью ОТКРЫТА.");
digitalWrite(statusLedPin, LOW); // Выключить LED
currentControlMode = MODE_AUTOMATIC; // Возвращаемся в авторежим после достижения концевика
} else if (millis() - motorStartTime > motorTimeoutMs) {
motorOff();
currentDoorState = DOOR_STOPPED_MID; // Ошибка или застряла
Serial.println("ОШИБКА: Таймаут открытия дверцы!");
digitalWrite(statusLedPin, LOW); // Выключить LED
}
break;
case DOOR_CLOSING:
if (isDoorClosedSensorActive) {
motorOff();
currentDoorState = DOOR_CLOSED;
Serial.println("Дверца полностью ЗАКРЫТА.");
digitalWrite(statusLedPin, LOW); // Выключить LED
currentControlMode = MODE_AUTOMATIC; // Возвращаемся в авторежим после достижения концевика
} else if (millis() - motorStartTime > motorTimeoutMs) {
motorOff();
currentDoorState = DOOR_STOPPED_MID; // Ошибка или застряла
Serial.println("ОШИБКА: Таймаут закрытия дверцы!");
digitalWrite(statusLedPin, LOW); // Выключить LED
}
break;
case DOOR_OPEN:
// Если дверца открыта, и мы в автоматическом режиме, проверяем сумерки
if (currentControlMode == MODE_AUTOMATIC && ldrValue < duskThreshold) {
Serial.print("Сумерки (LDR: "); Serial.print(ldrValue); Serial.println("). Инициирую закрытие.");
startClosingDoor();
}
break;
case DOOR_CLOSED:
// Если дверца закрыта, и мы в автоматическом режиме, проверяем рассвет
if (currentControlMode == MODE_AUTOMATIC && ldrValue > dawnThreshold) {
Serial.print("Рассвет (LDR: "); Serial.print(ldrValue); Serial.println("). Инициирую открытие.");
startOpeningDoor();
}
break;
case DOOR_STOPPED_MID:
// В этом состоянии дверца будет ждать ручного управления или условий авторежима,
// если она была остановлена из-за таймаута или ручной остановки.
// Если в автоматическом режиме, и условия изменились, может начать движение.
// Если была ручная остановка, то currentControlMode будет MANUAL, и нужно ручное вмешательство.
break;
}
// Вывод текущего значения LDR для отладки
// Serial.print("LDR: "); Serial.print(ldrValue);
// Serial.print(", State: ");
// switch (currentDoorState) {
// case DOOR_OPEN: Serial.print("OPEN"); break;
// case DOOR_CLOSED: Serial.print("CLOSED"); break;
// case DOOR_OPENING: Serial.print("OPENING"); break;
// case DOOR_CLOSING: Serial.print("CLOSING"); break;
// case DOOR_STOPPED_MID: Serial.print("STOPPED_MID"); break;
// }
// Serial.print(", Mode: ");
// Serial.println(currentControlMode == MODE_AUTOMATIC ? "AUTO" : "MANUAL");
delay(100); // Небольшая задержка для стабильности и снижения нагрузки на CPU
}