Плеко / Робототехніка
Урок 05-02 · Крок 1 / 10

Світлофор із кнопкою

Продовжуємо урок 05-01: додаємо кнопку запиту пішохода. Без запиту горить зелений для машин; натискання запускає перехід до червоного.

  • Arduino Uno та USB-кабель.
  • Червоний, жовтий і зелений LED; три резистори по 220 Ом.
  • Кнопка, макетна плата, дроти «тато–тато».

Спочатку пройди 05-01 — автоматичний світлофор. Навчимося зчитувати кнопку й відраховувати час за допомогою millis().

Це модель сигналів для автомобілів. Збираємо три незалежні гілки LED і повторюємо кнопку з уроку 2.

Перед зміною схеми від’єднай живлення. У Wokwi зупини симуляцію. Справжню схему перевір разом з учителем перед підключенням USB.

Повний код і файли проєкту

Запит із кнопки запускає послідовність сигналів без довгих delay.

#include <Arduino.h> // Команди плати Arduino.

const byte RED_PIN = 8; // Червоний LED.
const byte YELLOW_PIN = 9; // Жовтий LED.
const byte GREEN_PIN = 10; // Зелений LED.
const byte BUTTON_PIN = 2; // Кнопка замикає D2 на GND.
const unsigned long GREEN_MS = 5000; // Мінімум 5 секунд зеленого.
const unsigned long BLINK_MS = 3000; // Три блимання по одній секунді.
const unsigned long HALF_BLINK_MS = 500; // Пів секунди темно, пів секунди світло.
const unsigned long YELLOW_MS = 2000; // 2 секунди жовтого.
const unsigned long RED_MS = 5000; // 5 секунд для переходу.
const unsigned long READY_MS = 1000; // 1 секунда червоного з жовтим.
const unsigned long DEBOUNCE_MS = 30; // Чекаємо стабільного контакту.
enum Phase { GREEN, BLINK, YELLOW, RED, READY }; // Імена п’яти станів.
Phase phase = GREEN; // Починаємо із зеленого.
bool crossingRequested = false; // Чи є запит на перехід.
bool lastReading = HIGH; // Попереднє читання кнопки.
bool buttonState = HIGH; // Стабільний стан кнопки.
unsigned long buttonChangedAt = 0; // Коли змінився контакт кнопки.
unsigned long phaseStartedAt = 0; // Коли почався поточний стан.

void showLights(unsigned long elapsed) {
  digitalWrite(RED_PIN, phase == RED || phase == READY); // Червоний у двох станах.
  digitalWrite(YELLOW_PIN, phase == YELLOW || phase == READY); // Жовтий у двох станах.
  bool flashOn = (elapsed / HALF_BLINK_MS) % 2 == 1; // Чергуємо темну та світлу половини секунди.
  bool greenOn = phase == GREEN || (phase == BLINK && flashOn); // Зелений горить постійно або блимає.
  digitalWrite(GREEN_PIN, greenOn); // Передаємо обчислений стан на зелений LED.
}

void setup() { // Підготовка після ввімкнення.
  pinMode(RED_PIN, OUTPUT); // Вихід червоного LED.
  pinMode(YELLOW_PIN, OUTPUT); // Вихід жовтого LED.
  pinMode(GREEN_PIN, OUTPUT); // Вихід зеленого LED.
  pinMode(BUTTON_PIN, INPUT_PULLUP); // Відпущена кнопка дає HIGH.
  phaseStartedAt = millis(); // Запам’ятовуємо початок зеленого.
  showLights(0); // Показуємо початковий стан.
}

void readButton(unsigned long now) {
  bool reading = digitalRead(BUTTON_PIN); // Читаємо контакт.

  if (reading != lastReading) { // Контакт щойно змінився.
    lastReading = reading; // Зберігаємо нове читання.
    buttonChangedAt = now; // Починаємо відлік стабілізації.
  }

  if (now - buttonChangedAt >= DEBOUNCE_MS && reading != buttonState) { // Стан стабільний.
    buttonState = reading; // Приймаємо зміну кнопки.

    if (buttonState == LOW && phase == GREEN) { // Нове натискання на зеленому.
      crossingRequested = true; // Зберігаємо запит навіть після відпускання.
    }

  }
}

void updatePhase(unsigned long now) {
  unsigned long elapsed = now - phaseStartedAt; // Тривалість поточного стану.

  if (phase == GREEN && crossingRequested && elapsed >= GREEN_MS) { // Можна завершити зелений.
    phase = BLINK; // Спершу три блимання зеленого.

  } else if (phase == BLINK && elapsed >= BLINK_MS) { // Три блимання завершились.
    phase = YELLOW; // Після них вмикаємо жовтий.

  } else if (phase == YELLOW && elapsed >= YELLOW_MS) { // Жовтий завершився.
    phase = RED; // Вмикаємо червоний.

  } else if (phase == RED && elapsed >= RED_MS) { // Час переходу завершився.
    phase = READY; // Червоний разом із жовтим.

  } else if (phase == READY && elapsed >= READY_MS) { // Повертаємо рух.
    phase = GREEN; // Знову зелений.
    crossingRequested = false; // Старий запит виконано.

  } else { // Час зміни ще не настав.
    return; // Залишаємо поточний стан.
  }

  phaseStartedAt = now; // Запам’ятовуємо початок нового стану.
}

void loop() {
  unsigned long now = millis(); // Один показ часу для всіх дій цього оберту.
  readButton(now); // Читаємо кнопку та запам’ятовуємо новий запит.
  updatePhase(now); // Перемикаємо стан, якщо настав його час.
  showLights(now - phaseStartedAt); // Показуємо кольори та оновлюємо блимання.
}

Wokwi потребує інтернету. Завантажений HTML-урок працює офлайн.