# Решения задач Arduino C++
Комплект решений по задачам из файла «Кнопки-светодиоды». Каждая задача оформлена как материал для публикации: краткое условие, картинка, идея решения, подключение и код.
## Технические допущения
- Код написан для Arduino C++.
- Кнопка подключается к `D2` и `GND`, используется `INPUT_PULLUP`. Поэтому нажатая кнопка читается как `LOW`.
- Для светодиодов используются резисторы около `220 Ом`.
- Для моторов принят пример с драйвером L298N/TB6612-подобной схемы: `ENA=D5`, `IN1=D6`, `IN2=D7`, `IN3=D8`, `IN4=D9`, `ENB=D10`. В реальном комплекте пины можно заменить в начале кода.
- Для сервопривода используется `Servo.h`, пин `D6`. Углы открытия/закрытия нужно подогнать под механику клешни.
- Для УЗ-датчика принят HC-SR04: `Trig=D12`, `Echo=D11`.
- Для датчика света принят аналоговый вход `A0`, порог `threshold` нужно подобрать калибровкой.
- В задачах с поворотами по времени значения `delay()` помечены как калибруемые: без энкодера точный угол зависит от конкретного робота, батареи и поверхности.
## Кнопки-светодиоды №1.1. Подключаем светодиод
![Кнопки-светодиоды №1.1 — Подключаем светодиод](images/01-button-led-toggle.png)
### Подключение
- D2: кнопка -> GND
- D9: LED через 220 Ом
- GND: общий провод
- INPUT_PULLUP для кнопки

### Алгоритм
1. Начальное состояние: светодиод выключен.
2. Кнопка подключена к D2 и GND, используется INPUT_PULLUP.
3. После стабильного нажатия LOW состояние ledState меняется на противоположное.
4. digitalWrite записывает новое состояние на пин светодиода.

### Код Arduino C++
```cpp
const byte buttonPin = 2;
const byte ledPin = 9;

bool ledState = LOW;
bool lastStableButton = HIGH;
bool lastReading = HIGH;

unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, ledState);
}

void loop() {
  bool reading = digitalRead(buttonPin);

  if (reading != lastReading) {
    lastDebounceTime = millis();
  }

  if (millis() - lastDebounceTime > debounceDelay) {
    if (reading != lastStableButton) {
      lastStableButton = reading;
      if (lastStableButton == LOW) {
        ledState = !ledState;
        digitalWrite(ledPin, ledState);
      }
    }
  }

  lastReading = reading;
}
```

## Кнопки-светодиоды №1.2. Жёлтый и зелёный
![Кнопки-светодиоды №1.2 — Жёлтый и зелёный](images/02-yellow-green.png)
### Подключение
- D2: кнопка -> GND
- D9: жёлтый LED
- D10: зелёный LED
- длинный клик: всё выключить

### Алгоритм
1. Начальное состояние: оба светодиода выключены.
2. Короткий клик переключает состояния светодиодов на противоположные.
3. Первый клик включает жёлтый, второй выключает жёлтый и включает зелёный.
4. Длинное нажатие выключает оба светодиода.

### Код Arduino C++
```cpp
const byte yellowLed = 9;
const byte greenLed = 10;

// Общие функции для задач с кнопкой
struct ButtonEvent {
  bool click = false;
  bool longClick = false;
};

const byte buttonPin = 2;
const unsigned long debounceDelay = 50;
const unsigned long longPressDelay = 700;

bool lastReading = HIGH;
bool stableState = HIGH;
bool pressHandled = false;
unsigned long lastDebounceTime = 0;
unsigned long pressStartTime = 0;

ButtonEvent readButton() {
  ButtonEvent event;
  bool reading = digitalRead(buttonPin);

  if (reading != lastReading) {
    lastDebounceTime = millis();
  }

  if (millis() - lastDebounceTime > debounceDelay) {
    if (reading != stableState) {
      stableState = reading;

      if (stableState == LOW) {
        pressStartTime = millis();
        pressHandled = false;
      } else {
        unsigned long pressTime = millis() - pressStartTime;
        if (!pressHandled && pressTime < longPressDelay) {
          event.click = true;
        }
      }
    }

    if (stableState == LOW && !pressHandled && millis() - pressStartTime >= longPressDelay) {
      pressHandled = true;
      event.longClick = true;
    }
  }

  lastReading = reading;
  return event;
}

bool yellowOn = false;
bool greenOn = false;

void applyLeds() {
  digitalWrite(yellowLed, yellowOn);
  digitalWrite(greenLed, greenOn);
}

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(yellowLed, OUTPUT);
  pinMode(greenLed, OUTPUT);
  applyLeds();
}

void loop() {
  ButtonEvent button = readButton();

  if (button.click) {
    yellowOn = !yellowOn;
    greenOn = !greenOn;
    applyLeds();
  }

  if (button.longClick) {
    yellowOn = false;
    greenOn = false;
    applyLeds();
  }
}
```

## Кнопка-моторчик №1.1. Смена курса
![Кнопка-моторчик №1.1 — Смена курса](images/03-change-course.png)
### Подключение
- D2: кнопка
- D3/D4: LED
- D5-D10: драйвер моторов
- L298N/TB6612-подобная схема

### Алгоритм
1. Первый короткий клик: зелёный LED, движение вперёд 50%.
2. Следующий короткий клик: движение назад 20%, состояние светодиодов меняется.
3. Каждый следующий клик переключает направление движения.
4. Длинное нажатие останавливает робота и выключает LED.

### Код Arduino C++
```cpp
const byte yellowLed = 3;
const byte greenLed = 4;

// Общие функции для задач с кнопкой
struct ButtonEvent {
  bool click = false;
  bool longClick = false;
};

const byte buttonPin = 2;
const unsigned long debounceDelay = 50;
const unsigned long longPressDelay = 700;

bool lastReading = HIGH;
bool stableState = HIGH;
bool pressHandled = false;
unsigned long lastDebounceTime = 0;
unsigned long pressStartTime = 0;

ButtonEvent readButton() {
  ButtonEvent event;
  bool reading = digitalRead(buttonPin);

  if (reading != lastReading) {
    lastDebounceTime = millis();
  }

  if (millis() - lastDebounceTime > debounceDelay) {
    if (reading != stableState) {
      stableState = reading;

      if (stableState == LOW) {
        pressStartTime = millis();
        pressHandled = false;
      } else {
        unsigned long pressTime = millis() - pressStartTime;
        if (!pressHandled && pressTime < longPressDelay) {
          event.click = true;
        }
      }
    }

    if (stableState == LOW && !pressHandled && millis() - pressStartTime >= longPressDelay) {
      pressHandled = true;
      event.longClick = true;
    }
  }

  lastReading = reading;
  return event;
}

// Пример для драйвера L298N / TB6612-подобной схемы
const byte leftEn = 5;
const byte leftIn1 = 6;
const byte leftIn2 = 7;
const byte rightEn = 10;
const byte rightIn1 = 8;
const byte rightIn2 = 9;

int pwmFromPercent(int percent) {
  return map(constrain(percent, 0, 100), 0, 100, 0, 255);
}

void setMotor(byte en, byte in1, byte in2, int speedPercent) {
  int pwm = pwmFromPercent(abs(speedPercent));
  if (speedPercent > 0) {
    digitalWrite(in1, HIGH);
    digitalWrite(in2, LOW);
  } else if (speedPercent < 0) {
    digitalWrite(in1, LOW);
    digitalWrite(in2, HIGH);
  } else {
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
  }
  analogWrite(en, pwm);
}

void drive(int leftSpeed, int rightSpeed) {
  setMotor(leftEn, leftIn1, leftIn2, leftSpeed);
  setMotor(rightEn, rightIn1, rightIn2, rightSpeed);
}

void stopMotors() {
  drive(0, 0);
}

bool forwardState = false;

void showState() {
  digitalWrite(greenLed, forwardState);
  digitalWrite(yellowLed, !forwardState);
}

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(yellowLed, OUTPUT);
  pinMode(greenLed, OUTPUT);
  pinMode(leftEn, OUTPUT); pinMode(leftIn1, OUTPUT); pinMode(leftIn2, OUTPUT);
  pinMode(rightEn, OUTPUT); pinMode(rightIn1, OUTPUT); pinMode(rightIn2, OUTPUT);
  stopMotors();
}

void loop() {
  ButtonEvent button = readButton();

  if (button.click) {
    forwardState = !forwardState;
    showState();
    if (forwardState) drive(50, 50);
    else drive(-20, -20);
  }

  if (button.longClick) {
    stopMotors();
    digitalWrite(yellowLed, LOW);
    digitalWrite(greenLed, LOW);
  }
}
```

## Кнопка-моторчик №1.2. Считаем повороты
![Кнопка-моторчик №1.2 — Считаем повороты](images/04-count-turns.png)
### Подключение
- D2: кнопка
- D3: жёлтый LED
- D5-D10: моторы
- delay для 90/360 градусов калибруется

### Алгоритм
1. Первый клик запускает программу: LED загорается, левое колесо делает 360 градусов.
2. Следующие клики увеличивают количество миганий и поворотов на 90 градусов.
3. Длинный клик запускает выполнение накопленного количества действий.
4. Перед каждым поворотом мотор полностью останавливается.

### Код Arduino C++
```cpp
const byte yellowLed = 3;

// Общие функции для задач с кнопкой
struct ButtonEvent {
  bool click = false;
  bool longClick = false;
};

const byte buttonPin = 2;
const unsigned long debounceDelay = 50;
const unsigned long longPressDelay = 700;

bool lastReading = HIGH;
bool stableState = HIGH;
bool pressHandled = false;
unsigned long lastDebounceTime = 0;
unsigned long pressStartTime = 0;

ButtonEvent readButton() {
  ButtonEvent event;
  bool reading = digitalRead(buttonPin);

  if (reading != lastReading) {
    lastDebounceTime = millis();
  }

  if (millis() - lastDebounceTime > debounceDelay) {
    if (reading != stableState) {
      stableState = reading;

      if (stableState == LOW) {
        pressStartTime = millis();
        pressHandled = false;
      } else {
        unsigned long pressTime = millis() - pressStartTime;
        if (!pressHandled && pressTime < longPressDelay) {
          event.click = true;
        }
      }
    }

    if (stableState == LOW && !pressHandled && millis() - pressStartTime >= longPressDelay) {
      pressHandled = true;
      event.longClick = true;
    }
  }

  lastReading = reading;
  return event;
}

// Пример для драйвера L298N / TB6612-подобной схемы
const byte leftEn = 5;
const byte leftIn1 = 6;
const byte leftIn2 = 7;
const byte rightEn = 10;
const byte rightIn1 = 8;
const byte rightIn2 = 9;

int pwmFromPercent(int percent) {
  return map(constrain(percent, 0, 100), 0, 100, 0, 255);
}

void setMotor(byte en, byte in1, byte in2, int speedPercent) {
  int pwm = pwmFromPercent(abs(speedPercent));
  if (speedPercent > 0) {
    digitalWrite(in1, HIGH);
    digitalWrite(in2, LOW);
  } else if (speedPercent < 0) {
    digitalWrite(in1, LOW);
    digitalWrite(in2, HIGH);
  } else {
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
  }
  analogWrite(en, pwm);
}

void drive(int leftSpeed, int rightSpeed) {
  setMotor(leftEn, leftIn1, leftIn2, leftSpeed);
  setMotor(rightEn, rightIn1, rightIn2, rightSpeed);
}

void stopMotors() {
  drive(0, 0);
}

int turnCount = 0;
bool ready = false;

void blinkYellow(int count) {
  for (int i = 0; i < count; i++) {
    digitalWrite(yellowLed, HIGH);
    delay(250);
    digitalWrite(yellowLed, LOW);
    delay(250);
  }
}

void turnLeftWheel90() {
  drive(30, 0);
  delay(350); // Подбирается экспериментально под конкретного робота.
  stopMotors();
  delay(150);
}

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(yellowLed, OUTPUT);
  pinMode(leftEn, OUTPUT); pinMode(leftIn1, OUTPUT); pinMode(leftIn2, OUTPUT);
  pinMode(rightEn, OUTPUT); pinMode(rightIn1, OUTPUT); pinMode(rightIn2, OUTPUT);
  stopMotors();
}

void loop() {
  ButtonEvent button = readButton();

  if (button.click && !ready) {
    ready = true;
    digitalWrite(yellowLed, HIGH);
    drive(30, 0);
    delay(1400); // Один оборот левого колеса: калибруется.
    stopMotors();
    digitalWrite(yellowLed, LOW);
  } else if (button.click) {
    turnCount++;
  }

  if (button.longClick && ready) {
    blinkYellow(turnCount);
    for (int i = 0; i < turnCount; i++) {
      turnLeftWheel90();
    }
    turnCount = 0;
  }
}
```

## Кнопка-моторчик №1.3. Пульт управления
![Кнопка-моторчик №1.3 — Пульт управления](images/05-remote-control.png)
### Подключение
- D2: кнопка
- D3/D4: LED
- D5-D10: драйвер моторов
- цепочка: вперёд/вправо/вперёд/влево

### Алгоритм
1. Клики переводят робота по цепочке состояний.
2. Вперёд: оба светодиода горят, скорость 50%.
3. Поворот вправо: зелёный LED, танковый поворот 20%.
4. Поворот влево: жёлтый LED, танковый поворот 20%.
5. Длинное нажатие: стоп и выключить LED.

### Код Arduino C++
```cpp
const byte yellowLed = 3;
const byte greenLed = 4;

// Общие функции для задач с кнопкой
struct ButtonEvent {
  bool click = false;
  bool longClick = false;
};

const byte buttonPin = 2;
const unsigned long debounceDelay = 50;
const unsigned long longPressDelay = 700;

bool lastReading = HIGH;
bool stableState = HIGH;
bool pressHandled = false;
unsigned long lastDebounceTime = 0;
unsigned long pressStartTime = 0;

ButtonEvent readButton() {
  ButtonEvent event;
  bool reading = digitalRead(buttonPin);

  if (reading != lastReading) {
    lastDebounceTime = millis();
  }

  if (millis() - lastDebounceTime > debounceDelay) {
    if (reading != stableState) {
      stableState = reading;

      if (stableState == LOW) {
        pressStartTime = millis();
        pressHandled = false;
      } else {
        unsigned long pressTime = millis() - pressStartTime;
        if (!pressHandled && pressTime < longPressDelay) {
          event.click = true;
        }
      }
    }

    if (stableState == LOW && !pressHandled && millis() - pressStartTime >= longPressDelay) {
      pressHandled = true;
      event.longClick = true;
    }
  }

  lastReading = reading;
  return event;
}

// Пример для драйвера L298N / TB6612-подобной схемы
const byte leftEn = 5;
const byte leftIn1 = 6;
const byte leftIn2 = 7;
const byte rightEn = 10;
const byte rightIn1 = 8;
const byte rightIn2 = 9;

int pwmFromPercent(int percent) {
  return map(constrain(percent, 0, 100), 0, 100, 0, 255);
}

void setMotor(byte en, byte in1, byte in2, int speedPercent) {
  int pwm = pwmFromPercent(abs(speedPercent));
  if (speedPercent > 0) {
    digitalWrite(in1, HIGH);
    digitalWrite(in2, LOW);
  } else if (speedPercent < 0) {
    digitalWrite(in1, LOW);
    digitalWrite(in2, HIGH);
  } else {
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
  }
  analogWrite(en, pwm);
}

void drive(int leftSpeed, int rightSpeed) {
  setMotor(leftEn, leftIn1, leftIn2, leftSpeed);
  setMotor(rightEn, rightIn1, rightIn2, rightSpeed);
}

void stopMotors() {
  drive(0, 0);
}

byte state = 0; // 0 стоп, 1 вперёд, 2 вправо, 3 вперёд, 4 влево

void applyState() {
  digitalWrite(yellowLed, LOW);
  digitalWrite(greenLed, LOW);

  if (state == 1 || state == 3) {
    digitalWrite(yellowLed, HIGH);
    digitalWrite(greenLed, HIGH);
    drive(50, 50);
  } else if (state == 2) {
    digitalWrite(greenLed, HIGH);
    drive(20, -20);
  } else if (state == 4) {
    digitalWrite(yellowLed, HIGH);
    drive(-20, 20);
  } else {
    stopMotors();
  }
}

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(yellowLed, OUTPUT);
  pinMode(greenLed, OUTPUT);
  pinMode(leftEn, OUTPUT); pinMode(leftIn1, OUTPUT); pinMode(leftIn2, OUTPUT);
  pinMode(rightEn, OUTPUT); pinMode(rightIn1, OUTPUT); pinMode(rightIn2, OUTPUT);
  applyState();
}

void loop() {
  ButtonEvent button = readButton();

  if (button.click) {
    state++;
    if (state > 4) state = 1;
    applyState();
  }

  if (button.longClick) {
    state = 0;
    applyState();
  }
}
```

## Кнопка-сервопривод №1.1. Захват груза
![Кнопка-сервопривод №1.1 — Захват груза](images/06-cargo-grip.png)
### Подключение
- D2: кнопка
- D6: сервопривод
- D9/D10: LED
- Servo.h

### Алгоритм
1. Начальное состояние: клешня открыта.
2. Первый клик: жёлтый LED мигает 5 раз за секунду.
3. После мигания клешня закрывается на максимальной скорости.
4. Следующий клик: зелёный LED горит, клешня медленно открывается.

### Код Arduino C++
```cpp
#include <Servo.h>

const byte yellowLed = 9;
const byte greenLed = 10;
const byte servoPin = 6;
const int openAngle = 20;
const int closedAngle = 110;

// Общие функции для задач с кнопкой
struct ButtonEvent {
  bool click = false;
  bool longClick = false;
};

const byte buttonPin = 2;
const unsigned long debounceDelay = 50;
const unsigned long longPressDelay = 700;

bool lastReading = HIGH;
bool stableState = HIGH;
bool pressHandled = false;
unsigned long lastDebounceTime = 0;
unsigned long pressStartTime = 0;

ButtonEvent readButton() {
  ButtonEvent event;
  bool reading = digitalRead(buttonPin);

  if (reading != lastReading) {
    lastDebounceTime = millis();
  }

  if (millis() - lastDebounceTime > debounceDelay) {
    if (reading != stableState) {
      stableState = reading;

      if (stableState == LOW) {
        pressStartTime = millis();
        pressHandled = false;
      } else {
        unsigned long pressTime = millis() - pressStartTime;
        if (!pressHandled && pressTime < longPressDelay) {
          event.click = true;
        }
      }
    }

    if (stableState == LOW && !pressHandled && millis() - pressStartTime >= longPressDelay) {
      pressHandled = true;
      event.longClick = true;
    }
  }

  lastReading = reading;
  return event;
}

Servo claw;
bool closed = false;

void blinkYellowFast() {
  for (int i = 0; i < 5; i++) {
    digitalWrite(yellowLed, HIGH);
    delay(100);
    digitalWrite(yellowLed, LOW);
    delay(100);
  }
}

void moveServoSmooth(int fromAngle, int toAngle, int stepDelay) {
  int step = fromAngle < toAngle ? 1 : -1;
  for (int a = fromAngle; a != toAngle; a += step) {
    claw.write(a);
    delay(stepDelay);
  }
  claw.write(toAngle);
}

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(yellowLed, OUTPUT);
  pinMode(greenLed, OUTPUT);
  claw.attach(servoPin);
  claw.write(openAngle);
}

void loop() {
  ButtonEvent button = readButton();

  if (button.click && !closed) {
    blinkYellowFast();
    moveServoSmooth(openAngle, closedAngle, 2);
    closed = true;
  } else if (button.click && closed) {
    digitalWrite(greenLed, HIGH);
    moveServoSmooth(closedAngle, openAngle, 20);
    digitalWrite(greenLed, LOW);
    closed = false;
  }
}
```

## Кнопка-сервопривод №1.2. Доставка почты
![Кнопка-сервопривод №1.2 — Доставка почты](images/07-mail-delivery.png)
### Подключение
- D2: кнопка
- D6: сервопривод
- D9/D10: LED
- D5-D10: моторы

### Алгоритм
1. Короткий клик запускает последовательность действий.
2. Открыть клешню, выполнить танковый поворот 180 градусов.
3. Проехать вперёд 1 секунду со скоростью 30%.
4. Закрыть клешню, пока жёлтый LED мигает раз в полсекунды.

### Код Arduino C++
```cpp
#include <Servo.h>

const byte yellowLed = 9;
const byte greenLed = 10;
const byte servoPin = 6;
const int openAngle = 20;
const int closedAngle = 110;

// Общие функции для задач с кнопкой
struct ButtonEvent {
  bool click = false;
  bool longClick = false;
};

const byte buttonPin = 2;
const unsigned long debounceDelay = 50;
const unsigned long longPressDelay = 700;

bool lastReading = HIGH;
bool stableState = HIGH;
bool pressHandled = false;
unsigned long lastDebounceTime = 0;
unsigned long pressStartTime = 0;

ButtonEvent readButton() {
  ButtonEvent event;
  bool reading = digitalRead(buttonPin);

  if (reading != lastReading) {
    lastDebounceTime = millis();
  }

  if (millis() - lastDebounceTime > debounceDelay) {
    if (reading != stableState) {
      stableState = reading;

      if (stableState == LOW) {
        pressStartTime = millis();
        pressHandled = false;
      } else {
        unsigned long pressTime = millis() - pressStartTime;
        if (!pressHandled && pressTime < longPressDelay) {
          event.click = true;
        }
      }
    }

    if (stableState == LOW && !pressHandled && millis() - pressStartTime >= longPressDelay) {
      pressHandled = true;
      event.longClick = true;
    }
  }

  lastReading = reading;
  return event;
}

// Пример для драйвера L298N / TB6612-подобной схемы
const byte leftEn = 5;
const byte leftIn1 = 6;
const byte leftIn2 = 7;
const byte rightEn = 10;
const byte rightIn1 = 8;
const byte rightIn2 = 9;

int pwmFromPercent(int percent) {
  return map(constrain(percent, 0, 100), 0, 100, 0, 255);
}

void setMotor(byte en, byte in1, byte in2, int speedPercent) {
  int pwm = pwmFromPercent(abs(speedPercent));
  if (speedPercent > 0) {
    digitalWrite(in1, HIGH);
    digitalWrite(in2, LOW);
  } else if (speedPercent < 0) {
    digitalWrite(in1, LOW);
    digitalWrite(in2, HIGH);
  } else {
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
  }
  analogWrite(en, pwm);
}

void drive(int leftSpeed, int rightSpeed) {
  setMotor(leftEn, leftIn1, leftIn2, leftSpeed);
  setMotor(rightEn, rightIn1, rightIn2, rightSpeed);
}

void stopMotors() {
  drive(0, 0);
}

Servo claw;

void moveServoSmooth(int fromAngle, int toAngle, int stepDelay) {
  int step = fromAngle < toAngle ? 1 : -1;
  for (int a = fromAngle; a != toAngle; a += step) {
    claw.write(a);
    delay(stepDelay);
  }
  claw.write(toAngle);
}

void blinkWhileClosing() {
  for (int a = openAngle; a <= closedAngle; a++) {
    claw.write(a);
    digitalWrite(yellowLed, (a / 10) % 2);
    delay(20);
  }
  digitalWrite(yellowLed, LOW);
}

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(yellowLed, OUTPUT);
  pinMode(greenLed, OUTPUT);
  pinMode(leftEn, OUTPUT); pinMode(leftIn1, OUTPUT); pinMode(leftIn2, OUTPUT);
  pinMode(rightEn, OUTPUT); pinMode(rightIn1, OUTPUT); pinMode(rightIn2, OUTPUT);
  claw.attach(servoPin);
  claw.write(openAngle);
  stopMotors();
}

void loop() {
  ButtonEvent button = readButton();

  if (button.click) {
    digitalWrite(greenLed, HIGH);
    moveServoSmooth(closedAngle, openAngle, 5);
    drive(30, -30);
    delay(900); // Танковый поворот на 180 градусов: калибруется.
    drive(30, 30);
    delay(1000);
    stopMotors();
    digitalWrite(greenLed, LOW);

    blinkWhileClosing();
  }
}
```

## УЗ датчик №1.1. Далеко-близко
![УЗ датчик №1.1 — Далеко-близко](images/08-ultrasonic-distance.png)
### Подключение
- D12: Trig
- D11: Echo
- D9: жёлтый LED
- D10: зелёный LED

### Алгоритм
1. До 30 см: состояние близко, горит зелёный LED.
2. От 30 до 60 см: состояние среднее, горит жёлтый LED.
3. Больше 60 см: состояние далеко, оба LED мигают раз в секунду.
4. Расстояние считается по длительности импульса Echo.

### Код Arduino C++
```cpp
const byte trigPin = 12;
const byte echoPin = 11;
const byte yellowLed = 9;
const byte greenLed = 10;

unsigned long lastBlink = 0;
bool blinkState = false;

float distanceCm() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  unsigned long duration = pulseIn(echoPin, HIGH, 30000);
  if (duration == 0) return 999;
  return duration / 58.0;
}

void setup() {
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(yellowLed, OUTPUT);
  pinMode(greenLed, OUTPUT);
}

void loop() {
  float d = distanceCm();

  if (d < 30) {
    digitalWrite(greenLed, HIGH);
    digitalWrite(yellowLed, LOW);
  } else if (d < 60) {
    digitalWrite(greenLed, LOW);
    digitalWrite(yellowLed, HIGH);
  } else {
    if (millis() - lastBlink >= 500) {
      lastBlink = millis();
      blinkState = !blinkState;
      digitalWrite(greenLed, blinkState);
      digitalWrite(yellowLed, blinkState);
    }
  }
}
```

## УЗ датчик №1.2. Догонялки
![УЗ датчик №1.2 — Догонялки](images/09-catch-up.png)
### Подключение
- D2: кнопка
- D12/D11: Trig/Echo
- D3/D4: LED
- D5-D10: моторы

### Алгоритм
1. Короткое нажатие запускает программу.
2. Близко: жёлтый LED, робот едет назад 70%.
3. Средне: LED выключены, робот стоит.
4. Далеко: зелёный LED, робот едет вперёд; скорость зависит от расстояния.

### Код Arduino C++
```cpp
const byte trigPin = 12;
const byte echoPin = 11;
const byte yellowLed = 3;
const byte greenLed = 4;

// Общие функции для задач с кнопкой
struct ButtonEvent {
  bool click = false;
  bool longClick = false;
};

const byte buttonPin = 2;
const unsigned long debounceDelay = 50;
const unsigned long longPressDelay = 700;

bool lastReading = HIGH;
bool stableState = HIGH;
bool pressHandled = false;
unsigned long lastDebounceTime = 0;
unsigned long pressStartTime = 0;

ButtonEvent readButton() {
  ButtonEvent event;
  bool reading = digitalRead(buttonPin);

  if (reading != lastReading) {
    lastDebounceTime = millis();
  }

  if (millis() - lastDebounceTime > debounceDelay) {
    if (reading != stableState) {
      stableState = reading;

      if (stableState == LOW) {
        pressStartTime = millis();
        pressHandled = false;
      } else {
        unsigned long pressTime = millis() - pressStartTime;
        if (!pressHandled && pressTime < longPressDelay) {
          event.click = true;
        }
      }
    }

    if (stableState == LOW && !pressHandled && millis() - pressStartTime >= longPressDelay) {
      pressHandled = true;
      event.longClick = true;
    }
  }

  lastReading = reading;
  return event;
}

// Пример для драйвера L298N / TB6612-подобной схемы
const byte leftEn = 5;
const byte leftIn1 = 6;
const byte leftIn2 = 7;
const byte rightEn = 10;
const byte rightIn1 = 8;
const byte rightIn2 = 9;

int pwmFromPercent(int percent) {
  return map(constrain(percent, 0, 100), 0, 100, 0, 255);
}

void setMotor(byte en, byte in1, byte in2, int speedPercent) {
  int pwm = pwmFromPercent(abs(speedPercent));
  if (speedPercent > 0) {
    digitalWrite(in1, HIGH);
    digitalWrite(in2, LOW);
  } else if (speedPercent < 0) {
    digitalWrite(in1, LOW);
    digitalWrite(in2, HIGH);
  } else {
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
  }
  analogWrite(en, pwm);
}

void drive(int leftSpeed, int rightSpeed) {
  setMotor(leftEn, leftIn1, leftIn2, leftSpeed);
  setMotor(rightEn, rightIn1, rightIn2, rightSpeed);
}

void stopMotors() {
  drive(0, 0);
}

bool running = false;

float distanceCm() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  unsigned long duration = pulseIn(echoPin, HIGH, 30000);
  if (duration == 0) return 999;
  return duration / 58.0;
}

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(yellowLed, OUTPUT);
  pinMode(greenLed, OUTPUT);
  pinMode(leftEn, OUTPUT); pinMode(leftIn1, OUTPUT); pinMode(leftIn2, OUTPUT);
  pinMode(rightEn, OUTPUT); pinMode(rightIn1, OUTPUT); pinMode(rightIn2, OUTPUT);
  stopMotors();
}

void loop() {
  ButtonEvent button = readButton();
  if (button.click) running = true;
  if (!running) return;

  float d = distanceCm();

  if (d < 30) {
    digitalWrite(yellowLed, HIGH);
    digitalWrite(greenLed, LOW);
    drive(-70, -70);
  } else if (d < 60) {
    digitalWrite(yellowLed, LOW);
    digitalWrite(greenLed, LOW);
    stopMotors();
  } else {
    digitalWrite(yellowLed, LOW);
    digitalWrite(greenLed, HIGH);
    int speed = constrain(map((int)d, 60, 100, 0, 75), 0, 75);
    drive(speed, speed);
  }
}
```

## Датчик света №1.1. Вижу - не вижу
![Датчик света №1.1 — Вижу - не вижу](images/10-light-see.png)
### Подключение
- A0: датчик света
- D2: кнопка
- D9/D10: LED
- threshold подбирается калибровкой

### Алгоритм
1. Короткий клик запускает программу.
2. Значения датчика сравниваются с порогом threshold.
3. Белый цвет: горит зелёный LED.
4. Чёрный цвет: зелёный выключен, горит жёлтый LED.

### Код Arduino C++
```cpp
const byte lightPin = A0;
const byte yellowLed = 9;
const byte greenLed = 10;
const int threshold = 500; // Подобрать после калибровки.

// Общие функции для задач с кнопкой
struct ButtonEvent {
  bool click = false;
  bool longClick = false;
};

const byte buttonPin = 2;
const unsigned long debounceDelay = 50;
const unsigned long longPressDelay = 700;

bool lastReading = HIGH;
bool stableState = HIGH;
bool pressHandled = false;
unsigned long lastDebounceTime = 0;
unsigned long pressStartTime = 0;

ButtonEvent readButton() {
  ButtonEvent event;
  bool reading = digitalRead(buttonPin);

  if (reading != lastReading) {
    lastDebounceTime = millis();
  }

  if (millis() - lastDebounceTime > debounceDelay) {
    if (reading != stableState) {
      stableState = reading;

      if (stableState == LOW) {
        pressStartTime = millis();
        pressHandled = false;
      } else {
        unsigned long pressTime = millis() - pressStartTime;
        if (!pressHandled && pressTime < longPressDelay) {
          event.click = true;
        }
      }
    }

    if (stableState == LOW && !pressHandled && millis() - pressStartTime >= longPressDelay) {
      pressHandled = true;
      event.longClick = true;
    }
  }

  lastReading = reading;
  return event;
}

bool running = false;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(yellowLed, OUTPUT);
  pinMode(greenLed, OUTPUT);
}

void loop() {
  ButtonEvent button = readButton();
  if (button.click) running = true;
  if (!running) return;

  int value = analogRead(lightPin);

  if (value > threshold) {
    digitalWrite(greenLed, HIGH);
    digitalWrite(yellowLed, LOW);
  } else {
    digitalWrite(greenLed, LOW);
    digitalWrite(yellowLed, HIGH);
  }
}
```

## Датчик света №1.2. Аккуратное движение
![Датчик света №1.2 — Аккуратное движение](images/11-careful-motion.png)
### Подключение
- A0: датчик света
- D2: кнопка
- D3/D4: LED
- D5-D10: моторы

### Алгоритм
1. Короткий клик запускает движение.
2. Белый цвет: зелёный LED, движение вперёд 50%.
3. Чёрный цвет: скорость 10%, зелёный выключен.
4. Пока виден чёрный цвет, жёлтый LED мигает 2 раза в секунду.

### Код Arduino C++
```cpp
const byte lightPin = A0;
const byte yellowLed = 3;
const byte greenLed = 4;
const int threshold = 500; // Подобрать после калибровки.

// Общие функции для задач с кнопкой
struct ButtonEvent {
  bool click = false;
  bool longClick = false;
};

const byte buttonPin = 2;
const unsigned long debounceDelay = 50;
const unsigned long longPressDelay = 700;

bool lastReading = HIGH;
bool stableState = HIGH;
bool pressHandled = false;
unsigned long lastDebounceTime = 0;
unsigned long pressStartTime = 0;

ButtonEvent readButton() {
  ButtonEvent event;
  bool reading = digitalRead(buttonPin);

  if (reading != lastReading) {
    lastDebounceTime = millis();
  }

  if (millis() - lastDebounceTime > debounceDelay) {
    if (reading != stableState) {
      stableState = reading;

      if (stableState == LOW) {
        pressStartTime = millis();
        pressHandled = false;
      } else {
        unsigned long pressTime = millis() - pressStartTime;
        if (!pressHandled && pressTime < longPressDelay) {
          event.click = true;
        }
      }
    }

    if (stableState == LOW && !pressHandled && millis() - pressStartTime >= longPressDelay) {
      pressHandled = true;
      event.longClick = true;
    }
  }

  lastReading = reading;
  return event;
}

// Пример для драйвера L298N / TB6612-подобной схемы
const byte leftEn = 5;
const byte leftIn1 = 6;
const byte leftIn2 = 7;
const byte rightEn = 10;
const byte rightIn1 = 8;
const byte rightIn2 = 9;

int pwmFromPercent(int percent) {
  return map(constrain(percent, 0, 100), 0, 100, 0, 255);
}

void setMotor(byte en, byte in1, byte in2, int speedPercent) {
  int pwm = pwmFromPercent(abs(speedPercent));
  if (speedPercent > 0) {
    digitalWrite(in1, HIGH);
    digitalWrite(in2, LOW);
  } else if (speedPercent < 0) {
    digitalWrite(in1, LOW);
    digitalWrite(in2, HIGH);
  } else {
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
  }
  analogWrite(en, pwm);
}

void drive(int leftSpeed, int rightSpeed) {
  setMotor(leftEn, leftIn1, leftIn2, leftSpeed);
  setMotor(rightEn, rightIn1, rightIn2, rightSpeed);
}

void stopMotors() {
  drive(0, 0);
}

bool running = false;
unsigned long lastBlink = 0;
bool yellowBlink = false;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(yellowLed, OUTPUT);
  pinMode(greenLed, OUTPUT);
  pinMode(leftEn, OUTPUT); pinMode(leftIn1, OUTPUT); pinMode(leftIn2, OUTPUT);
  pinMode(rightEn, OUTPUT); pinMode(rightIn1, OUTPUT); pinMode(rightIn2, OUTPUT);
  stopMotors();
}

void loop() {
  ButtonEvent button = readButton();
  if (button.click) running = true;
  if (!running) return;

  int value = analogRead(lightPin);

  if (value > threshold) {
    digitalWrite(greenLed, HIGH);
    digitalWrite(yellowLed, LOW);
    drive(50, 50);
  } else {
    digitalWrite(greenLed, LOW);
    drive(10, 10);

    if (millis() - lastBlink >= 250) {
      lastBlink = millis();
      yellowBlink = !yellowBlink;
      digitalWrite(yellowLed, yellowBlink);
    }
  }
}
```

