GPIO: мигаем светодиодом

«Blink» — мигающий светодиод — это «Hello, world» железа. С него начинают все, кто осваивает микроконтроллеры.

Заставить вывод то включаться, то выключаться — кажется мелочью. Но это первый раз, когда твой код двигает физический мир. Дальше всё строится на этом.

Цифровой вывод GPIO работает как программируемый выключатель. Команда «сделать вывод HIGH» подаёт на него 3,3 В, «LOW» — снимает напряжение. Подключив светодиод (через резистор!) к выводу, мы можем зажигать и гасить его кодом. А настроив вывод на вход, можно читать состояние кнопки.

Логика мигания проста: включить вывод, подождать, выключить, подождать, повторить. Эта пауза задаёт частоту мигания. На том же принципе работает чтение кнопки: программа в цикле проверяет, есть ли напряжение на входе.

Как это работает под капотом

   ESP32 GPIO2 ----[220 Ом]----|>|----GND
                                LED

   кнопка:
   3.3В ---[кнопка]--- GPIO4 ---[10к]--- GND
                       (pull-down подтяжка)

Когда вывод настроен на выход и установлен в HIGH, через резистор и светодиод течёт ток — диод светится. В LOW тока нет — гаснет. Для кнопки важен подтягивающий резистор: без него «отпущенный» вход висит в воздухе и ловит случайные помехи. Pull-down притягивает вход к нулю, пока кнопка не нажата; pull-up — к питанию. У ESP32 такие резисторы встроены и включаются программно.

Вот как выглядит классический blink на двух языках. Код исполняется на самом ESP32, поэтому он помечен как текст.

// Arduino C++ — мигаем светодиодом на GPIO2
void setup() {
  pinMode(2, OUTPUT);
}
void loop() {
  digitalWrite(2, HIGH);  // включить
  delay(500);             // подождать 0.5 с
  digitalWrite(2, LOW);   // выключить
  delay(500);
}
# MicroPython — то же самое
from machine import Pin
from time import sleep

led = Pin(2, Pin.OUT)
while True:
    led.value(1)   # включить
    sleep(0.5)
    led.value(0)   # выключить
    sleep(0.5)

Логику «нажата ли кнопка» удобно отлаживать обычным Python в браузере, прежде чем переносить на железо. Запусти врезку «Посчитай сам ▶».

# Логика обработки кнопки с антидребезгом (имитация)
readings = [0, 0, 1, 0, 1, 1, 1, 1, 0, 0]  # сырые показания входа

stable = 0
count = 0
THRESHOLD = 3   # сколько подряд одинаковых нужно, чтобы поверить

for r in readings:
    if r == stable:
        count = 0
    else:
        count += 1
        if count >= THRESHOLD:
            stable = r
            count = 0
            print(f"Кнопка перешла в состояние: {'нажата' if r else 'отпущена'}")

print(f"Итоговое стабильное состояние: {stable}")

Эта врезка показывает антидребезг: механическая кнопка «дрожит» при нажатии, и мы считаем нажатие реальным только после нескольких одинаковых показаний подряд.

Частые ошибки

  • Светодиод без резистора. Та же ошибка, что и в разделе про компоненты — диод сгорает.
  • Вход без подтяжки. Кнопка «срабатывает сама» от помех.
  • Дребезг кнопки. Одно нажатие читается как несколько — нужен антидребезг.

Best practices

  • Включай встроенные pull-up/pull-down вместо внешних резисторов, где можно.
  • Добавляй антидребезг (программный или конденсатором) для надёжного чтения кнопок.
  • Не блокируй цикл длинными delay в реальных проектах — используй таймеры.

Где это встречается

За простым миганием стоит мощная идея — широтно-импульсная модуляция (ШИМ, PWM). Если включать и выключать вывод не раз в секунду, а тысячи раз, и менять долю времени «включено», глаз увидит не мигание, а плавную яркость. Так на цифровом выводе, который умеет лишь «вкл/выкл», получают плавную регулировку света и скорости моторов. ШИМ — прямое развитие обычного blink.

Чтение кнопок тоже глубже, чем кажется. В реальных проектах вместо опроса в цикле используют прерывания: вывод сам «дёргает» программу, когда меняет состояние, не заставляя процессор постоянно проверять его. Это экономит ресурсы и позволяет реагировать мгновенно. Освоив базовый blink и чтение кнопки с антидребезгом, ты получаешь фундамент, на котором стоят ШИМ, прерывания и работа с любыми цифровыми сигналами.

Запомни главное

  • Цифровой GPIO включается и выключается кодом — так мигает светодиод.
  • Вход кнопки нужно подтягивать резистором (pull-up/pull-down).
  • Дребезг контактов гасят антидребезгом, а blink развивается в ШИМ.
  • Не блокируй цикл длинными delay — в реальных проектах используй таймеры.

Итог: цифровой GPIO включается и выключается кодом — так мигает светодиод и читается кнопка; вход нужно подтягивать резистором, а дребезг — гасить. Это фундамент любого проекта. Но многие датчики аналоговые, и их читает АЦП — об этом следующий урок.

Проверьте себя
1. Зачем кнопке нужен подтягивающий (pull-up или pull-down) резистор?
AЧтобы кнопка светилась
BЧтобы неподключённый вход не ловил помехи и имел чёткое состояние
CЧтобы увеличить напряжение
DРезистор не нужен
2. Что такое дребезг (bounce) кнопки?
AКнопка светится
BМеханические контакты «дрожат», и одно нажатие читается как несколько
CКнопка не работает
DЭто звук нажатия