Цель: добавить сенсорную кнопку (TTP223) для переключения между нормальным режимом работы и режимом «отключён» (мигающий жёлтый 1 Гц), реализовав приоритет обработки кнопки — нажатие мгновенно прерывает любую логику и переключает режим безопасно и предсказуемо.
HIGH
при касании)Подключение:
TTP223: VCC → 5V, GND → GND, OUT → D2
(Arduino Uno)
По‑прежнему используем две последовательно соединённые LED одного цвета без резисторов (как в прошлом уроке).
loop()
обрабатываем кнопку (с дебаунсом и детекцией фронта).loop()
в этом тике.Такой порядок обеспечивает отзывчивость и детерминированность: нажатие не «теряется» внутри фаз.
#include <Arduino.h>
// ---------------- Пины ----------------
const uint8_t PIN_RED = 10;
const uint8_t PIN_YELLOW = 9;
const uint8_t PIN_GREEN = 8;
const uint8_t PIN_BUTTON = 2; // TTP223 OUT (ACTIVE HIGH)
// ---------------- Тайминги (мс) ----------------
// Нормальный режим (ГОСТ)
const uint32_t T_GREEN_STEADY = 7000; // Зелёный перед миганием
const uint32_t T_GREEN_BLINK_TOTAL = 3000; // Мигание зелёного суммарно 3 с
const uint32_t GREEN_BLINK_HALF = 500; // 1 Гц: 500 мс ON/OFF -> полный цикл 1 с
const uint32_t T_YELLOW = 3000; // Жёлтый строго 3 с
const uint32_t T_RED = 7000; // Красный (пример)
const uint32_t T_RED_YELLOW = 2000; // Красный+жёлтый (<= 2 с)
// Режим "Отключён"
const uint32_t DISABLED_BLINK_HALF = 500; // Жёлтый мигает 1 Гц
// Кнопка
const uint32_t DEBOUNCE_MS = 50; // Защита от дребезга/повторов
// ---------------- Состояния ----------------
enum Phase : uint8_t {
PH_GREEN = 0,
PH_GREEN_BLINK,
PH_YELLOW,
PH_RED,
PH_RED_YELLOW
};
enum Mode : uint8_t {
MODE_NORMAL = 0, // по ГОСТ
MODE_DISABLED // мигающий жёлтый
};
// ---------------- Глобальные переменные ----------------
Mode mode = MODE_NORMAL;
Phase phase = PH_GREEN;
uint32_t phaseStart = 0;
// Мигание в разных режимах
uint32_t greenBlinkTick = 0;
bool greenOn = true;
uint32_t disabledTick = 0;
bool yellowOn = false;
// Дебаунс кнопки (детектор фронта)
bool buttonPressedEdge() {
static uint8_t lastRaw = LOW;
static uint8_t debounced = LOW;
static uint32_t lastChange = 0;
uint32_t now = millis();
uint8_t raw = digitalRead(PIN_BUTTON);
if (raw != lastRaw) { // обнаружили изменение уровня
lastRaw = raw;
lastChange = now; // отметим время изменения
}
if ((now - lastChange) > DEBOUNCE_MS) {
if (debounced != raw) { // уровень стабилизировался -> обновим
debounced = raw;
if (debounced == HIGH) { // фронт нажатия
return true;
}
}
}
return false;
}
// Управление огнями светофора
void setLights(bool r, bool y, bool g) {
digitalWrite(PIN_RED, r ? HIGH : LOW);
digitalWrite(PIN_YELLOW, y ? HIGH : LOW);
digitalWrite(PIN_GREEN, g ? HIGH : LOW);
}
// Инициализация режимов
void enterMode(Mode newMode) {
mode = newMode;
uint32_t now = millis();
if (mode == MODE_NORMAL) {
// старт цикла по ГОСТ: зелёный
phase = PH_GREEN;
phaseStart = now;
greenBlinkTick = now;
greenOn = true;
setLights(false, false, true);
} else {
// отключён: мигающий жёлтый
disabledTick = now;
yellowOn = false;
setLights(false, false, false); // начнём с погашенного жёлтого
}
}
// Обновление нормального режима (ГОСТ)
void updateNormal() {
uint32_t now = millis();
uint32_t elapsed = now - phaseStart;
switch (phase) {
case PH_GREEN:
if (elapsed >= T_GREEN_STEADY) {
phase = PH_GREEN_BLINK;
phaseStart = now;
greenBlinkTick = now;
greenOn = true;
setLights(false, false, true);
}
break;
case PH_GREEN_BLINK:
if ((now - greenBlinkTick) >= GREEN_BLINK_HALF) {
greenBlinkTick = now;
greenOn = !greenOn;
setLights(false, false, greenOn);
}
if (elapsed >= T_GREEN_BLINK_TOTAL) {
phase = PH_YELLOW;
phaseStart = now;
setLights(false, true, false);
}
break;
case PH_YELLOW:
if (elapsed >= T_YELLOW) {
phase = PH_RED;
phaseStart = now;
setLights(true, false, false);
}
break;
case PH_RED:
if (elapsed >= T_RED) {
phase = PH_RED_YELLOW;
phaseStart = now;
setLights(true, true, false);
}
break;
case PH_RED_YELLOW:
if (elapsed >= T_RED_YELLOW) {
phase = PH_GREEN;
phaseStart = now;
setLights(false, false, true);
}
break;
}
}
// Обновление режима "Отключён" (мигающий жёлтый)
void updateDisabled() {
uint32_t now = millis();
if ((now - disabledTick) >= DISABLED_BLINK_HALF) {
disabledTick = now;
yellowOn = !yellowOn;
setLights(false, yellowOn, false);
}
}
void setup() {
pinMode(PIN_RED, OUTPUT);
pinMode(PIN_YELLOW, OUTPUT);
pinMode(PIN_GREEN, OUTPUT);
pinMode(PIN_BUTTON, INPUT); // TTP223 даёт HIGH при касании
enterMode(MODE_NORMAL); // стартуем в нормальном режиме
}
void loop() {
// 1) ПРИОРИТЕТ КНОПКИ: обрабатываем первой
if (buttonPressedEdge()) {
// Тоггл режимов
if (mode == MODE_NORMAL) {
enterMode(MODE_DISABLED);
} else {
enterMode(MODE_NORMAL);
}
return; // мгновенно выходим: никакой другой логики в этот тик
}
// 2) Обновляем активный режим
if (mode == MODE_NORMAL) {
updateNormal();
} else {
updateDisabled();
}
}
Дополнительные библиотеки не требуются — используется стандартное ядро Arduino.
Если вместо TTP223 используется механическая кнопка, лучше подключить её к
INPUT_PULLUP
(кнопка на землю) и инвертировать логикуbuttonPressedEdge()
(считать фронтLOW
).
Почему мигание 1 Гц — это по 500 мс ON/OFF?
Так полный цикл «вкл+выкл» равен 1 секунде и даёт 3 вспышки за 3 секунды, как требует условие.
Зачем return
после обработки кнопки?
Чтобы нажатие имело абсолютный приоритет: никакая другая логика не выполнится в текущем тике.