// Обзовем все используемые пины через константы, чтобы легче читались
const int PIN_LOW_BTN = 13;
const int PIN_4WD_BTN = 14;
const int PIN_IN2 = 2;
const int PIN_IN3 = 3;
const int PIN_IN4 = 4;
const int PIN_IN5 = 5;
const int PIN_MOTOR_FWD = 7;
const int PIN_MOTOR_REV = 8;
const int PIN_LAMP_4WD = 10;
const int PIN_LAMP_LOW = 11;
const int PIN_BUZZER = 25;
/*
/ Логика таблицы отслеживает только три состояния коробки, в которых двигатель всегда СТОП.
/ Состояния (по таблице):
/Входы (2–3–4–5) Состояние
/ 1 1 0 1 2WD
/ 0 1 1 0 4WD
/ 0 0 1 1 4WD LOW
/ Из состояния 2WD двигатель можно включить только вперед, из состояния 4WD LOW - только назад.
/ Из состояния 4WD можно включить в обе стороны.
/ Контрольные состояния соберем в перечисление, а разбирать будем через switch-case
*/
enum GearState
{
UNKNOWN,
STATE_2WD,
STATE_4WD,
STATE_4WD_LOW,
STATE_ERROR
};
/* Функция для опроса состояния входов, вернет текущее состояние
/ или UNKNOWN, если процесс переключения не завершен или завершен не успешо,
/ например, сгорел двигатель в коробке, или что-то заклинило.
/ Здесь этот вариант не обрабатывается.
*/
GearState currentState()
{
bool in2 = digitalRead(PIN_IN2);
bool in3 = digitalRead(PIN_IN3);
bool in4 = digitalRead(PIN_IN4);
bool in5 = digitalRead(PIN_IN5);
if (in2 && in3 && !in4 && in5)
return STATE_2WD;
if (!in2 && in3 && in4 && !in5)
return STATE_4WD;
if (!in2 && !in3 && in4 && in5)
return STATE_4WD_LOW;
else
return STATE_ERROR;
return UNKNOWN;
}
bool isPressed(int pin) // опрос кнопки
{
return digitalRead(pin) == LOW; // Если кнопки с PULLUP, без PULLUP "==LOW" убрать
}
void setup()
{ // настройка пинов
pinMode(PIN_MOTOR_FWD, OUTPUT);
pinMode(PIN_MOTOR_REV, OUTPUT);
pinMode(PIN_LAMP_4WD, OUTPUT);
pinMode(PIN_LAMP_LOW, OUTPUT);
pinMode(PIN_BUZZER, OUTPUT);
pinMode(PIN_LOW_BTN, INPUT_PULLUP); // это если вход подтянут к питанию,
pinMode(PIN_4WD_BTN, INPUT_PULLUP); // если к земле через внешний резистор, то просто INPUT
pinMode(PIN_IN2, INPUT);
pinMode(PIN_IN3, INPUT);
pinMode(PIN_IN4, INPUT);
pinMode(PIN_IN5, INPUT);
digitalWrite(PIN_MOTOR_FWD, LOW);
digitalWrite(PIN_MOTOR_REV, LOW);
}
void loop()
{
bool lowBtn = isPressed(PIN_LOW_BTN); // опрашиваем состояние кнопок
bool wdBtn = isPressed(PIN_4WD_BTN);
if (lowBtn && wdBtn)
return; // защита от дурака: одновременное нажатие — ничего не делаем
GearState state = currentState(); // получаем текущее состояние входов
switch (state)
{
case STATE_2WD:
/*
/ Из этого состояния может двигаться только вперед,
/ к положению 4WD, поэтому проверяем только одну кнопку
*/
digitalWrite(PIN_LAMP_4WD, LOW);// вывод на табло состояния РКПП
digitalWrite(PIN_LAMP_LOW, LOW);
if (wdBtn)
{
// Если нажата кнопка 4WD, переключаемся 2WD -> 4WD (вперёд)
digitalWrite(PIN_MOTOR_FWD, HIGH);// включаем мотор вперед
tone(PIN_BUZZER, 600); // звук процесса переключения
unsigned long lastMillis = millis();
while (state != STATE_4WD && !(millis() - lastMillis > 7000))// условия прекращения цикла - изменение статуса или истечение времени
{
state = currentState(); // получаем текущее состояние входов
delay(10);
}
digitalWrite(PIN_MOTOR_FWD, LOW); //останавливаем мотор
noTone(PIN_BUZZER); //выключаем звук
}
break;
case STATE_4WD:
digitalWrite(PIN_LAMP_4WD, HIGH);// опрос состояния РКПП, вывод на табло
digitalWrite(PIN_LAMP_LOW, LOW);
/*
/ Из этого положения можем двигаться как вперед к 4WD LOW,
/ так и назад к 2WD, поэтому контролируем обе кнопки
*/
if (wdBtn)
{
// Переход 4WD -> 2WD (назад)
digitalWrite(PIN_MOTOR_REV, HIGH);
tone(PIN_BUZZER, 600);
unsigned long lastMillis = millis();
while (state != STATE_2WD && !(millis() - lastMillis > 5000))
{
state = currentState();
delay(10);
}
digitalWrite(PIN_MOTOR_REV, LOW);
digitalWrite(PIN_LAMP_4WD, LOW);
digitalWrite(PIN_LAMP_LOW, LOW);
noTone(PIN_BUZZER);
}
else if (lowBtn)
{
// Переход 4WD -> 4WD LOW (вперёд)
digitalWrite(PIN_MOTOR_FWD, HIGH);
tone(PIN_BUZZER, 600);
unsigned long lastMillis = millis();
while (state != STATE_4WD_LOW && !(millis() - lastMillis > 5000))
{
state = currentState();
delay(10);
}
digitalWrite(PIN_MOTOR_FWD, LOW);
digitalWrite(PIN_LAMP_4WD, HIGH);
digitalWrite(PIN_LAMP_LOW, HIGH);
noTone(PIN_BUZZER);
}
break;
case STATE_4WD_LOW:
digitalWrite(PIN_LAMP_4WD, HIGH); // опрос состояния РКПП, вывод на табло
digitalWrite(PIN_LAMP_LOW, HIGH);
/*
/ Из этого положения можем двигаться только назад, к положению 4WD,
/ контролируем только одну кнопку
*/
if (lowBtn)
{
// Переход 4WD LOW -> 4WD (назад)
digitalWrite(PIN_MOTOR_REV, HIGH);
tone(PIN_BUZZER, 600);
unsigned long lastMillis = millis();
while (state != STATE_4WD && !(millis() - lastMillis > 5000))
{
state = currentState();
delay(10);
}
digitalWrite(PIN_MOTOR_REV, LOW);
digitalWrite(PIN_LAMP_4WD, HIGH);
digitalWrite(PIN_LAMP_LOW, LOW);
noTone(PIN_BUZZER);
}
break;
case STATE_ERROR: //состояние ошибки
digitalWrite(PIN_LAMP_4WD, HIGH); // в режиме неполного переключения моргают обе лампы, звучит буззер
digitalWrite(PIN_LAMP_LOW, HIGH);
tone(PIN_BUZZER, 600, 500);
delay (500);
digitalWrite(PIN_LAMP_4WD, LOW);
digitalWrite(PIN_LAMP_LOW, LOW);
tone(PIN_BUZZER, 1000, 500);
delay (500);
if (lowBtn) //нажимаем кнопку LOW
{
digitalWrite(PIN_MOTOR_REV, HIGH); //включаем двигатель назад на короткий промежуток времени, с целю "качнуть" вилку переключения
tone(PIN_BUZZER, 2000); // звук звучит в другой тональности
delay(3000); // время "качания " вилки
digitalWrite(PIN_MOTOR_REV, LOW);
noTone(PIN_BUZZER);
state = currentState();
delay(10);
}
if (wdBtn) // кнопка 4WD чтобы "качнуть" вилку в другую сторону
{
digitalWrite(PIN_MOTOR_FWD, HIGH);
tone(PIN_BUZZER, 2000);
delay(3000);
digitalWrite(PIN_MOTOR_FWD, LOW);
noTone(PIN_BUZZER);
state = currentState();
delay(10);
}
break;
default:
// В переходном или ошибочном состоянии ничего не делаем
break;
}
}