#158Средний

Светофор на Pico с выбором 4 режимов кнопкой (конечный автомат)

4 режима светофора всего одной кнопкой! Каждое нажатие меняет режим: обычный цикл (красный → жёлтый → зелёный по очереди), только красный (сигнал стоп), мигающий жёлтый (внимание/ночной режим) и выключено (обслуживание). Понятия: конечный автомат (переменная mode определяет, какое поведение выполняется), цикл режимов по модулю ((mode + 1) % 4 → 0→1→2→3→0), неблокирующий тайминг (фазы светодиодов отсчитываются через time.ticks_ms() + time.ticks_diff()).

5.0(1)
15 собрано
Светофор на Pico с выбором 4 режимов кнопкой (конечный автомат)
Настоящие светофоры ночью мигают жёлтым, а иногда горят только красным; в этом проекте каждый из этих режимов ты выбираешь одной кнопкой. Каждое нажатие переходит к следующему режиму: обычный цикл с красным, жёлтым и зелёным по очереди, только красный, мигающий жёлтый и полностью выключено. Raspberry Pi Pico продолжает управлять огнями и при этом слушает кнопку, поэтому ни одно нажатие не пропадает.

Видео

Схема

Светофор на Pico с выбором 4 режимов кнопкой (конечный автомат) - Circuit

Исходный код

MicroPython
1# ─────────────────────────────────────────
2#   BlueGrays · bluegrays.com
3#   Светофор на Pico с выбором 4 режимов кнопкой (конечный автомат)
4# ─────────────────────────────────────────
5# Одна кнопка переключает 4 режима: обычный цикл (красный -> жёлтый ->
6# зелёный), только красный, мигающий жёлтый и выкл. Использует конечный
7# автомат и неблокирующий отсчёт времени через time.ticks_ms(), поэтому
8# кнопка всегда отзывчива.
9#
10# Пины:
11#   GP11 -> Зелёный светодиод
12#   GP12 -> Жёлтый светодиод
13#   GP13 -> Красный светодиод (каждый светодиод с 220 ohm)
14#   GP4  -> Кнопка (другая ножка к GND)
15
16from machine import Pin
17import time
18
19# ============ СВЕТОДИОДЫ ============
20green  = Pin(11, Pin.OUT)   # Зелёный
21yellow = Pin(12, Pin.OUT)   # Жёлтый
22red    = Pin(13, Pin.OUT)   # Красный
23
24# ============ КНОПКА ============
25button = Pin(4, Pin.IN, Pin.PULL_UP)   # При нажатии читается 0
26
27# ============ СОСТОЯНИЕ ============
28# Режим: 0 обычный цикл, 1 только красный, 2 мигающий жёлтый, 3 выкл
29mode = 0
30last_btn = 1                 # Предыдущее значение кнопки
31blink_state = False          # Состояние мигающего жёлтого
32last_tick = time.ticks_ms()  # Последнее переключение мигания
33
34# ============ ОБЫЧНЫЙ ЦИКЛ ============
35normal_step = 0                     # 0: красный, 1: жёлтый, 2: зелёный
36normal_last = time.ticks_ms()       # Время начала шага
37NORMAL_TIMES = [3000, 1000, 3000]   # Красный, жёлтый, зелёный (мс)
38LEDS = [red, yellow, green]
39
40def all_off():
41    # Погасить все светодиоды
42    red.off(); yellow.off(); green.off()
43
44# ============ ГЛАВНЫЙ ЦИКЛ ============
45while True:
46    btn = button.value()
47
48    # ---- Кнопка нажата (1 -> 0): смена режима ----
49    if last_btn == 1 and btn == 0:
50        mode = (mode + 1) % 4
51        all_off()
52        normal_step = 0
53        normal_last = time.ticks_ms()
54        time.sleep_ms(200)   # Антидребезг
55    last_btn = btn
56
57    now = time.ticks_ms()
58
59    # ---- Обычный цикл светофора ----
60    if mode == 0:
61        if time.ticks_diff(now, normal_last) > NORMAL_TIMES[normal_step]:
62            normal_step = (normal_step + 1) % 3
63            normal_last = now
64        all_off()
65        LEDS[normal_step].on()
66
67    # ---- Только красный (стоп) ----
68    elif mode == 1:
69        all_off(); red.on()
70
71    # ---- Мигающий жёлтый, 0,4 с (осторожно/ночь) ----
72    elif mode == 2:
73        if time.ticks_diff(now, last_tick) > 400:
74            blink_state = not blink_state
75            yellow.value(blink_state)
76            last_tick = now
77            red.off(); green.off()
78
79    # ---- Выкл ----
80    else:
81        all_off()
82
83    time.sleep_ms(10)
84

Частые вопросы

Что такое конечный автомат?

Одна переменная (mode) определяет, что программа делает прямо сейчас. Каждое нажатие кнопки меняет режим, а цикл выполняет только код текущего режима. Это стандартный способ держать сложное поведение в порядке.

Что делает (mode + 1) % 4?

Она увеличивает режим на один и, дойдя до 4, возвращает его к 0, поэтому режимы бесконечно идут по кругу 0, 1, 2, 3, 0, 1...

Почему используется ticks_ms(), а не time.sleep()?

Во время sleep() Pico не может читать кнопку, поэтому смена режима запаздывает. ticks_ms() и ticks_diff() позволяют обойтись без долгих ожиданий во всех фазах светодиодов. Однако эта версия ждёт 200 ms для антидребезга при смене режима и 10 ms в конце каждого цикла; в эти промежутки кнопка заново не читается.

Можно ли добавить новый режим?

Да, увеличь число режимов до 5 и напиши для нового отдельный блок поведения, например мигающий зелёный или поочерёдное зажигание всех огней.