Проект: светофор и расписание по времени
Три светодиода превращаются в настоящий светофор, как только мы добавим логику переключения фаз по времени.
Светофор — идеальный первый "умный" проект: тут и несколько устройств, и расписание, и циклическая логика. Всё, как во взрослых системах.
Возьмём три светодиода — красный, жёлтый, зелёный — каждый со своим резистором на свой GPIO-пин. Задача: переключать фазы как настоящий светофор и держать каждую нужное время.
Светофор — это проект, который ты видел тысячи раз по дороге в школу, и именно поэтому он такой удобный для обучения: тебе не нужно объяснять, как он должен работать, ты и так знаешь. Красный держится долго, зелёный тоже, жёлтый — короткое предупреждение между ними, и всё это крутится по кругу без остановки. Когда абстрактное "напиши циклическую логику с таймингами" превращается в знакомый объект на столе, программирование перестаёт быть сухой теорией. А ещё это первый проект, где устройств больше одного — и приходится думать, чтобы они не мешали друг другу.
Как работает под капотом
GPIO17 --[330]-- (красный) -- GND GPIO27 --[330]-- (жёлтый) -- GND GPIO22 --[330]-- (зелёный) -- GND
Ключевая идея проекта — разделить данные и логику. "Какая фаза и сколько длится" — это данные, их удобно хранить отдельным списком. А "как перебирать фазы по кругу и выдавать сигналы" — это логика, она остаётся неизменной, даже если ты поменяешь тайминги. Такой подход называют управлением по таблице: меняешь числа в таблице — меняется поведение, и при этом не нужно трогать ни строчки рабочего кода. Так строят настоящие промышленные контроллеры.
ДАННЫЕ (расписание) ЛОГИКА (цикл) +------------------+ +------------------+ | красный - 5с | --> | бери фазу | | зелёный - 5с | --> | включи цвет | | жёлтый - 2с | --> | жди длительность | +------------------+ | -> следующая | меняем числа здесь код НЕ меняется
На Pi это выглядело бы так (пример для платы):
from gpiozero import LED
from time import sleep
red, yellow, green = LED(17), LED(27), LED(22)
while True:
red.on(); sleep(5); red.off()
green.on(); sleep(5); green.off()
yellow.on(); sleep(2); yellow.off()
Обрати внимание на важную деталь: перед включением новой фазы предыдущая обязательно гасится (red.off() до того, как загорится зелёный). Если этого не делать, в какой-то момент будут гореть два цвета сразу — а настоящий светофор так себя не ведёт. Управление несколькими устройствами всегда требует следить за тем, чтобы их состояния не конфликтовали.
А логику расписания — какая фаза, как долго и что дальше — отладим в браузере на чистом Python. Это сердце проекта, и оно от железа не зависит. Попробуй сам ▶
# Расписание фаз светофора по времени (в условных секундах)
schedule = [
("красный", 5),
("зелёный", 5),
("жёлтый", 2),
]
clock = 0
for cycle in range(2): # два полных круга
for color, duration in schedule:
print(f"t={clock:>2}с: горит {color} ({duration}с)")
clock += duration
print(f"Полный цикл занял {clock} условных секунд")
Мы храним расписание как список пар "цвет — длительность". Меняя числа, легко настроить тайминги, не трогая остальной код. Это и есть хорошая архитектура: данные отдельно, логика отдельно.
Давай покажем, насколько это гибко. Добавим в расписание пешеходную фазу и посчитаем, сколько таких циклов уложится в одну минуту — и всё это, не меняя ни строчки в самом цикле перебора. Поменялась только таблица данных:
# Расширенное расписание + подсчёт циклов за минуту
schedule = [
("красный", 5),
("зелёный", 4),
("жёлтый", 2),
("пешеходный", 3),
]
cycle_time = sum(duration for _, duration in schedule)
fits = 60 // cycle_time
print(f"Один цикл: {cycle_time}с")
print(f"За минуту уложится полных циклов: {fits}")
for color, duration in schedule:
print(f" {color:<10} -> {duration}с")
Частые ошибки
- Горят два цвета сразу. Перед включением новой фазы выключай предыдущую.
- Жёсткие числа в коде. Если тайминги "вшиты" повсюду, их тяжело менять — храни в списке/словаре.
- Перепутаны пины. Проверь, что цвет в коде соответствует физическому светодиоду.
- Забыли про бесконечность. Настоящий светофор работает по кругу — без
while Trueкод отработает один раз и остановится. - Слишком короткие фазы. Если поставить длительность в доли секунды, светофор будет "мигать" вместо нормального переключения — тайминги должны быть человеческими.
Best practices
- Выноси настройки (тайминги, пины) в начало файла или в структуру данных.
- Сначала отладь последовательность фаз на печати, потом подключай светодиоды.
- Подумай о расширении: пешеходная фаза, мигающий жёлтый ночью.
- Давай переменным понятные имена (
red,yellow), а неled1,led2— через неделю спасибо себе скажешь. - Проверяй, что сумма фаз даёт разумный полный цикл, прежде чем заливать на железо.
Итоги. Светофор — это несколько устройств плюс расписание фаз, и главное правило — гасить предыдущий цвет перед новым. Расписание удобно хранить данными (список пар) и отлаживать на чистом Python, а уже потом переносить на gpiozero. Разделив данные и логику, ты легко добавляешь новые фазы, не трогая рабочий код. Дальше перейдём к датчикам.