#170Новичок

Парктроник на Raspberry Pi Pico (HC-SR04 + 3 светодиода + зуммер)

Парктроник из автомобиля у тебя на столе! Raspberry Pi Pico с помощью ультразвукового датчика HC-SR04 измеряет расстояние до препятствия примерно 16 раз в секунду. По мере приближения по очереди загораются зелёный, жёлтый и красный светодиоды, а зуммер пищит всё чаще; ближе 10 см тон становится непрерывным, а красный светодиод мигает. Ультразвуковое измерение расстояния, звук через PWM и неблокирующие задержки в одном проекте.

0.0(0)
1 собрано
Парктроник на Raspberry Pi Pico (HC-SR04 + 3 светодиода + зуммер)
Настольная версия парктроника из автомобиля. HC-SR04 посылает звук слишком высокий для твоих ушей и слушает эхо, чтобы измерить, насколько далеко препятствие; как только что-то оказывается ближе метра, загорается зелёный светодиод и зуммер медленно пищит. При приближении к нему присоединяются жёлтый и красный светодиоды, сигналы становятся чаще и выше, а ближе 10 сантиметров тон не умолкает, пока мигает красный свет.

Видео

Схема

Парктроник на Raspberry Pi Pico (HC-SR04 + 3 светодиода + зуммер) - Circuit

Исходный код

MicroPython
1# ─────────────────────────────────────────
2#   BlueGrays · bluegrays.com
3#   Парктроник на Raspberry Pi Pico (HC-SR04 + 3 светодиода + зуммер)
4# ─────────────────────────────────────────
5# Парктроник. HC-SR04 измеряет расстояние до препятствия; по мере
6# приближения по очереди загораются зелёный, жёлтый и красный светодиоды,
7# а писки учащаются. Совсем близко тон становится непрерывным, а красный
8# светодиод мигает.
9#
10# Пины:
11#   GP27 -> HC-SR04 TRIG
12#   GP26 -> HC-SR04 ECHO (на реальном железе делитель 1k + 2k)
13#   VBUS -> HC-SR04 VCC (5V), GND.6 -> HC-SR04 GND
14#   GP4  -> 220 ohm -> красный светодиод (катод к GND)
15#   GP8  -> 220 ohm -> жёлтый светодиод (катод к GND)
16#   GP12 -> 220 ohm -> зелёный светодиод (катод к GND)
17#   GP17 -> Зуммер (+), другая ножка к GND
18
19from machine import Pin, PWM, time_pulse_us
20import time
21
22# ============ НАСТРОЙКИ ============
23GREEN_CM = 100         # Зелёный вкл, начинается писк
24YELLOW_CM = 50         # Подключается жёлтый
25RED_CM = 25            # Подключается красный
26STOP_CM = 10           # Непрерывный тон + мигающий красный
27
28BEEP_MS = 60           # Длительность одного писка
29SLOW_GAP_MS = 700      # Пауза между писками на GREEN_CM
30FAST_GAP_MS = 50       # Пауза между писками около STOP_CM
31VOLUME = 32768         # Заполнение PWM, 32768 = 50%
32FLASH_MS = 120         # Период мигания красного в зоне STOP
33
34MEASURE_MS = 60        # Интервал измерения, мин. 60 мс
35PRINT_MS = 250         # Интервал вывода в Serial
36ECHO_TIMEOUT_US = 12000  # дальше ~2 м = препятствия нет
37SOUND_CM_PER_US = 0.0343  # Скорость звука (20 C)
38LOOP_MS = 5            # Шаг главного цикла
39
40# Зоны от ближней к дальней: (имя, верхняя граница в см, (зелёный, жёлтый, красный), тон в Hz)
41ZONES = (
42    ("DUR / STOP", STOP_CM, (1, 1, 1), 1900),
43    ("KIRMIZI / RED", RED_CM, (1, 1, 1), 1900),
44    ("SARI / YELLOW", YELLOW_CM, (1, 1, 0), 1500),
45    ("YESIL / GREEN", GREEN_CM, (1, 0, 0), 1200),
46)
47CLEAR = len(ZONES)     # Зона «препятствия нет»
48STOP = 0               # Индекс зоны STOP
49
50# ============ ЖЕЛЕЗО ============
51trig = Pin(27, Pin.OUT, value=0)
52echo = Pin(26, Pin.IN)
53led_green = Pin(12, Pin.OUT, value=0)
54led_yellow = Pin(8, Pin.OUT, value=0)
55led_red = Pin(4, Pin.OUT, value=0)
56buzzer = PWM(Pin(17))
57buzzer.duty_u16(0)
58
59
60# ============ ИЗМЕРЕНИЕ ============
61def measure_cm():
62    # Отправить импульс TRIG 10 мкс, засечь, сколько ECHO остаётся в HIGH
63    trig.value(0)
64    time.sleep_us(2)
65    trig.value(1)
66    time.sleep_us(10)
67    trig.value(0)
68    pulse = time_pulse_us(echo, 1, ECHO_TIMEOUT_US)
69    if pulse < 0:
70        return None    # Нет эха или слишком далеко
71    # Звук идёт туда и обратно: половина
72    return pulse * SOUND_CM_PER_US / 2
73
74
75def median3(values):
76    # Медиана последних 3 показаний: одно плохое показание не заставляет светодиоды мерцать
77    a, b, c = values
78    return max(min(a, b), min(max(a, b), c))
79
80
81def zone_of(dist):
82    if dist is None:
83        return CLEAR
84    for i, zone in enumerate(ZONES):
85        if dist <= zone[1]:
86            return i
87    return CLEAR
88
89
90def beep_gap_ms(dist):
91    # Между STOP_CM и GREEN_CM пауза между писками линейно растёт от FAST до SLOW
92    ratio = (dist - STOP_CM) / (GREEN_CM - STOP_CM)
93    ratio = min(1.0, max(0.0, ratio))
94    return int(FAST_GAP_MS + ratio * (SLOW_GAP_MS - FAST_GAP_MS))
95
96
97# ============ ЗВУК ============
98tone_on = False
99tone_hz = 0
100
101
102def tone(hz):
103    # Не трогать PWM, если тон уже звучит
104    global tone_on, tone_hz
105    if tone_on and tone_hz == hz:
106        return
107    buzzer.freq(hz)
108    buzzer.duty_u16(VOLUME)
109    tone_on = True
110    tone_hz = hz
111
112
113def silence():
114    global tone_on
115    if tone_on:
116        buzzer.duty_u16(0)
117        tone_on = False
118
119
120def set_leds(green, yellow, red):
121    led_green.value(green)
122    led_yellow.value(yellow)
123    led_red.value(red)
124
125
126def self_test():
127    # Короткая проверка светодиодов и зуммера при включении: зелёный -> жёлтый -> красный
128    for leds, hz in (((1, 0, 0), 1200), ((1, 1, 0), 1500), ((1, 1, 1), 1900)):
129        set_leds(*leds)
130        tone(hz)
131        time.sleep_ms(90)
132        silence()
133        time.sleep_ms(160)
134    set_leds(0, 0, 0)
135
136
137# ============ ГЛАВНЫЙ ЦИКЛ ============
138print("== BlueGrays · bluegrays.com ==")
139print("Pico Park Sensoru / Parking Sensor")
140print("Yesil <= %d cm, sari <= %d cm, kirmizi <= %d cm, surekli ses <= %d cm"
141      % (GREEN_CM, YELLOW_CM, RED_CM, STOP_CM))
142print("Green <= %d cm, yellow <= %d cm, red <= %d cm, continuous tone <= %d cm"
143      % (GREEN_CM, YELLOW_CM, RED_CM, STOP_CM))
144self_test()
145
146FAR = 999.0                       # Заглушка для «препятствия нет»
147history = [FAR, FAR, FAR]         # Последние 3 показания
148distance = None                   # Отфильтрованное расстояние (см)
149zone = CLEAR
150last_zone = -1
151now = time.ticks_ms()
152next_measure = now
153next_print = now
154last_beep = time.ticks_add(now, -10000)   # Первый писк звучит сразу
155beep_end = now
156
157while True:
158    now = time.ticks_ms()
159
160    # 1) Измерение: каждые MEASURE_MS, с медианным фильтром
161    if time.ticks_diff(now, next_measure) >= 0:
162        next_measure = time.ticks_add(now, MEASURE_MS)
163        reading = measure_cm()
164        history = history[1:] + [reading if reading is not None else FAR]
165        filtered = median3(history)
166        distance = None if filtered >= FAR else filtered
167        zone = zone_of(distance)
168        if zone != last_zone:
169            name = "ENGEL YOK / CLEAR" if zone == CLEAR else ZONES[zone][0]
170            print("Bolge / Zone ->", name)
171            last_zone = zone
172
173    # 2) Светодиоды: шкала, в зоне STOP красный мигает
174    if zone == CLEAR:
175        set_leds(0, 0, 0)
176    elif zone == STOP:
177        flash_on = (now // FLASH_MS) % 2 == 0
178        set_leds(1, 1, 1 if flash_on else 0)
179    else:
180        set_leds(*ZONES[zone][2])
181
182    # 3) Звук: неблокирующий таймер писка
183    if zone == CLEAR:
184        silence()
185    elif zone == STOP:
186        tone(ZONES[STOP][3])          # Совсем близко: непрерывный тон
187    else:
188        if tone_on and time.ticks_diff(now, beep_end) >= 0:
189            silence()
190        # Пауза пересчитывается на каждом проходе: следующий писк ускоряется, как только расстояние
191        # уменьшается
192        period = BEEP_MS + beep_gap_ms(distance)
193        if not tone_on and time.ticks_diff(now, last_beep) >= period:
194            tone(ZONES[zone][3])
195            last_beep = now
196            beep_end = time.ticks_add(now, BEEP_MS)
197
198    # 4) Монитор порта: расстояние в см
199    if time.ticks_diff(now, next_print) >= 0:
200        next_print = time.ticks_add(now, PRINT_MS)
201        if distance is None:
202            print("Mesafe / Distance: --- (engel yok / no obstacle)")
203        else:
204            print("Mesafe / Distance: %5.1f cm" % distance)
205
206    time.sleep_ms(LOOP_MS)
207

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

Зачем на реальной схеме нужен делитель напряжения для ECHO?

HC-SR04 работает от 5V, и его выход ECHO тоже 5V, а пины Pico выдерживают не больше 3,3 V. Подключи ECHO к GP26 через 1 kΩ, а GP26 к GND через 2 kΩ, чтобы на пине было около 3,3 V. В Wokwi делитель не нужен.

Как изменить расстояния предупреждения?

Измени константы `GREEN_CM`, `YELLOW_CM`, `RED_CM` и `STOP_CM` в начале кода. Сохраняй порядок порогов `STOP_CM < RED_CM < YELLOW_CM < GREEN_CM`; например, используй `STOP_CM = 30`, `RED_CM = 50`, `YELLOW_CM = 100` и `GREEN_CM = 150`. Светодиоды и сигналы автоматически следуют новым порогам.

Почему сигналы учащаются плавно, а не по зонам?

`beep_gap_ms()` линейно масштабирует паузу по расстоянию: 700 ms при 100 см, 50 ms при 10 см. Она пересчитывается на каждом проходе главного цикла, поэтому следующий сигнал звучит раньше в тот же момент, когда препятствие приближается. Зоны меняют только светодиоды и высоту тона.

Нужно ли устанавливать какие-то библиотеки?

Нет. Функция `time_pulse_us()`, которая используется для измерения, входит во встроенный модуль `machine`. Прошей MicroPython на Pico и сохрани на него код как `main.py` в Thonny; датчик запустится сам с проверкой светодиодов и тремя сигналами.