Raspberry Pi Jam — світлофор на Raspberry Pi 3/4/5

Raspberry Pi Jam у КАІ Мейкерспейс. Вчимося керувати світлофором Pi-Stop із Python на Raspberry Pi 3, 4 або 5. Відкрий цю сторінку на самій Raspberry Pi — код звідси копіюється просто в термінал.

Про назву: бібліотека, якою ми керуємо світлом, зветься GPIO Zero (gpiozero). «Zero» тут — це назва бібліотеки, а не плата Raspberry Pi Zero. Вона однаково працює на Pi 3, Pi 4 і Pi 5, і саме на цих платах ми й працюємо.

Що потрібно

  • Raspberry Pi 3, 4 або 5 — усі три мають однаковий 40-піновий гребінець GPIO, тож код нижче для них ідентичний;
  • Raspberry Pi OS з робочим столом (Bookworm або новіша — та, що ставиться з Raspberry Pi Imager за замовчуванням);
  • плата-світлофор Pi-Stop;
  • ця сторінка, відкрита в браузері на Raspberry Pi (Chromium);
  • відкритий Термінал (чорна іконка з >_ у верхній панелі).

Яка в тебе плата і яка система

cat /proc/device-tree/model; echo; lsb_release -d

Має надрукувати щось на кшталт Raspberry Pi 4 Model B Rev 1.5 і назву системи. Якщо там Pi 5 — усе працює так само, але глянь окремий пункт про Pi 5 у розділі «Якщо щось не працює».

Як копіювати: тисни кнопку Копіювати в кутку блоку коду, переходь у Термінал і вставляй через Ctrl+Shift+V (або права кнопка миші → Paste). Потім Enter.

1. Підключення Pi-Stop

Вимкни Raspberry Pi, надінь Pi-Stop прямо на контакти GPIO і ввімкни живлення. Плата сідає на чотири контакти непарного ряду — того, що ближче до краю плати:

Pi-StopGPIO-пінФізичний пін
Червоний (R)2215
Жовтий (A)2713
Зелений (G)1711
Земля (GND)GND9

Ліва колонка — номери GPIO, саме їх ми пишемо в коді. Права — порядковий номер контакту на гребінці; він потрібен, якщо збираєш світлофор не платою Pi-Stop, а окремими світлодіодами на дротах. На Pi 3, 4 і 5 ця розкладка однакова.

Побачити всю гребінку саме своєї плати можна командою:

pinout
Увага: надівай і знімай плату лише на вимкненій Raspberry Pi. Якщо кольори загоряються не ті — швидше за все плата стоїть задом наперед або зсунута на один ряд пінів.

Перевірка, що бібліотека на місці

python3 -c "import gpiozero; print('gpiozero OK')"

Має надрукувати gpiozero OK. Якщо ні — дивись розділ «Якщо щось не працює» внизу.

Створюємо теку для програм

mkdir -p ~/svitlofor && cd ~/svitlofor

Усі наступні програми зберігатимуться сюди. Цю команду досить виконати один раз.

2. Перше блимання

Наступна команда створює файл 01-blink.py з програмою всередині. Копіюй її цілком — разом із рядками << 'EOF' та EOF.

cat > ~/svitlofor/01-blink.py << 'EOF'
from gpiozero import LED
from signal import pause

red = LED(22)

red.blink()

pause()
EOF

Запускаємо:

python3 ~/svitlofor/01-blink.py

Червоний має блимати без упину. Щоб зупинити програму — натисни Ctrl+C у терміналі.

Чому pause()? blink() блимає у фоні й не затримує програму. У терміналі програма без pause() закінчилась би миттєво — ти б навіть не побачив спалаху. pause() каже: «стій і чекай, поки мене не зупинять».

3. Три світла з різною швидкістю

cat > ~/svitlofor/02-blink3.py << 'EOF'
from gpiozero import LED
from signal import pause

red = LED(22)
amber = LED(27)
green = LED(17)

red.blink(1, 1)
amber.blink(2, 2)
green.blink(3, 3)

pause()
EOF
python3 ~/svitlofor/02-blink3.py

Числа в дужках — це секунди: перше — скільки світло горить, друге — скільки не горить. Тобто blink(3, 3) — найповільніше.

Питання: якщо більше число = повільніше, яке число зробить блимання швидшим? Спробуй blink(0.2, 0.2) — і зупини Ctrl+C.

4. Вмикаємо світла по черзі

Основні команди для світлодіода: on() — увімкнути, off() — вимкнути, toggle() — перемкнути, blink() — блимати. А sleep() робить паузу між командами.

cat > ~/svitlofor/03-sequence.py << 'EOF'
from gpiozero import LED
from time import sleep

red = LED(22)
amber = LED(27)
green = LED(17)

red.on()
sleep(1)
amber.on()
sleep(1)
green.on()
sleep(1)
red.off()
sleep(1)
amber.off()
sleep(1)
green.off()
EOF
python3 ~/svitlofor/03-sequence.py

Світла загоряються одне за одним, потім так само гаснуть — і програма завершується сама.

5. Робимо це нескінченним — цикл while

cat > ~/svitlofor/04-loop.py << 'EOF'
from gpiozero import LED
from time import sleep

red = LED(22)
amber = LED(27)
green = LED(17)

while True:
    red.on()
    sleep(1)
    amber.on()
    sleep(1)
    green.on()
    sleep(1)
    red.off()
    sleep(1)
    amber.off()
    sleep(1)
    green.off()
    sleep(1)
EOF
python3 ~/svitlofor/04-loop.py

while True: означає «повторюй вічно». Усе, що всередині циклу, зсунуте вправо на чотири пробіли — саме цей відступ і каже Python, що рядок належить циклу. Зупинка — Ctrl+C.

6. Завдання: справжній світлофор

Тепер ти вмієш вмикати світла окремо й керувати паузами. Збери справжню послідовність українського світлофора:

  1. Зелений — їдь
  2. Жовтий — готуйся спинятись
  3. Червоний — стій
  4. Червоний і жовтий разом — приготуйся їхати
  5. Зелений — і все спочатку

Подумай про час: скільки має горіти кожен стан? Зелений і червоний — довго, жовтий — коротко. Спробуй написати сам, і лише потім відкривай підказку.

Показати готовий розв’язок
cat > ~/svitlofor/05-svitlofor.py << 'EOF'
from gpiozero import LED
from time import sleep

red = LED(22)
amber = LED(27)
green = LED(17)


def all_off():
    red.off()
    amber.off()
    green.off()


try:
    all_off()
    while True:
        green.on()          # їдь
        sleep(5)
        green.off()

        amber.on()          # готуйся спинятись
        sleep(2)
        amber.off()

        red.on()            # стій
        sleep(5)

        amber.on()          # червоний + жовтий: приготуйся
        sleep(2)
        all_off()
except KeyboardInterrupt:
    all_off()
    print()
    print('Світлофор зупинено')
EOF
python3 ~/svitlofor/05-svitlofor.py

Блок try ... except KeyboardInterrupt потрібен, щоб після Ctrl+C світла гасли, а не лишались горіти.

7. Бонус: те саме одним рядком

У gpiozero вже є готовий клас саме для світлофора:

cat > ~/svitlofor/06-bonus.py << 'EOF'
from gpiozero import TrafficLights
from signal import pause

lights = TrafficLights(red=22, amber=27, green=17)

lights.blink()

pause()
EOF
python3 ~/svitlofor/06-bonus.py

Спробуй також lights.on(), lights.off(), lights.green.on() — і подивись документацію: gpiozero.readthedocs.io.

Куди далі

  • додати кнопку (Button) — щоб пішохід перемикав світло;
  • додати зумер (Buzzer) — щоб пищав на зелений;
  • зробити двосторонній перехрестя з двох Pi-Stop.

Якщо щось не працює

ModuleNotFoundError: No module named 'gpiozero'

sudo apt update && sudo apt install -y python3-gpiozero python3-lgpio

Став саме через apt. pip install gpiozero у свіжій Raspberry Pi OS впаде з помилкою externally-managed-environment — це нормально, так і задумано.

У мене Raspberry Pi 5, і щось поводиться інакше

Увесь код на цій сторінці на Pi 5 працює без змін — gpiozero сам звертається до нового контролера через lgpio. Але стара бібліотека RPi.GPIO на Pi 5 не працює взагалі: якщо знайдеш у мережі приклад із import RPi.GPIO, він упаде з помилкою про SOC peripheral base address. Це не поламана плата — просто той приклад старіший за Pi 5. Бери приклади на gpiozero.

Якщо gpiozero на Pi 5 скаржиться на pin factory:

sudo apt install -y python3-lgpio

Програма запустилась і одразу закінчилась, світло не блимає

У програмі з blink() забули pause() в кінці. Без нього програма завершується миттєво.

Помилка про «зайнятий» пін (GPIO busy / device or resource busy)

Десь ще працює попередня програма. Знайди її термінал і натисни Ctrl+C. Якщо не допомогло — перезавантаж Raspberry Pi.

Світла лишились горіти після зупинки

python3 -c "from gpiozero import LED
for p in (22, 27, 17):
    LED(p).off()"

IndentationError

З’їхали відступи всередині while. Створи файл ще раз кнопкою Копіювати — вона віддає код точно з відступами.

Хочу подивитись, що вийшло у файлі

cat ~/svitlofor/05-svitlofor.py

Матеріал побудовано на вільному ресурсі Raspberry Pi Foundation «Controlling a traffic lights sequence with GPIO Zero» (ліцензія CC BY-SA), адаптовано для роботи в терміналі Raspberry Pi. Питання й реєстрація на воркшопи — makerspace.kai.edu.ua/trainings.