Подключение кнопки к Ардуино
Кнопка — простейшее устройство ввода, с помощью которого можно дать команду Ардуино. Есть некоторые особенности подключения кнопок к плате Ардуино, которые мы разберём в этой статье. Также мы познакомимся с примерами программного кода, реализующими реакцию микроконтроллера на нажатие кнопки.
Краткое содержание:
- Виды кнопок
- Подключение кнопки
- Подтягивающий резистор
- Внутренняя подтяжка
- Простейший скетч: нажатие и отпускание
- Дребезг контактов: что это и почему мешает
- Программная фильтрация дребезга
- Аппаратная фильтрация (RC-цепочка)
- Библиотеки для кнопок: краткий обзор
Виды кнопок
Вообще, кнопка - это нечто, что умеет замыкать и размыкать контакты. Но есть разные механические реализации этого нехитрого устройства, о которых полезно знать.
Во-первых, кнопки делятся по типу фиксации на:
- Тактовые кнопки — без фиксации. Нажал - контакты замкнулись, отпустил - разомкнулись. Как пример - кнопка дверного звонка.
- Кнопки с фиксацией — нажал один раз — контакт замкнулся, нажал ещё раз — разомкнулся. Такие кнопки можно встретить в качестве выключателей питания на бытовой технике
- Клавишные кнопки — имеют два устойчивых положения, замкнуто/разомкнуто. Как пример - классический выключатель света.
Во-вторых, кнопки можно поделить по типу начального состояния:
- Нормально-разомкнутая, иногда говорят "замыкающая" (на английском NO, normally open). В изначальном состоянии цепь разомкнута.
- Нормально-замкнутая, иногда говорят "размыкающая" (NC, normally closed) - наоборот, в свободном состоянии замнута и размыкается при нажатии.

Обозначение некоторых типов кнопок на схемах: а) нормально-разомкнутая без фиксации б) нормально-замкнутая без фиксации в) нормально-разомкнутая с фиксацией
Есть и более сложные кнопки и переключатели. Например, которые управляют сразу несколькими группами контактов. Но, с точки зрения Ардуино, программирование всех видов кнопок весьма похоже: нужно просто "научить" микроконтроллер определять, замкнута ли та или иная пара контактов. Так что для простоты в данной статье мы рассмотрим работу с обычными тактовыми кнопками. Такие кнопки можно найти в каждом стандартном наборе Ардуино.

Стандартная кнопка из набора Ардуино. У каждого контакта есть пара выводов, для удобства закрепления кнопки на макетной плате. При нажатии на кнопку все 4 вывода оказываются замкнуты.
Подключение кнопки к плате Ардуино
На плате Ардуино есть пины (контакты), которые могуть работать как "вход" - то есть определять уровень напряжения, который на них подали. Режим пина задаётся в коде следующим образом:
// переключаем пин №7 в режим входа
pinMode(7, INPUT);
Поступим просто: подключим один контакт кнопки к положительному выводу питания, а другой - к пину Ардуино. Нажали кнопку - на пин попал высокий потенциал - осталось обработать это событие в программе.

Неправильное подключение кнопки к Ардуино: нет подтягивающего резистора
Однако, тут нас сразу поджидает неочевидная проблема. Пока кнопка отпущена, пин Ардуино ни к чему не подключён, он "висит в воздухе". Поскольку входы Ардуино очень чувствительны, они могут реагировать на случайные наводки. Например, на статическое электричество поблизости. Получается, кнопка не нажата - а Ардуино фиксирует сигнал. Ложное срабатывание.
Подтягивающий резистор
Чтобы этого не происходило, в схему добавляют так называемый подтягивающий резистор (в англоязычной литературе pull-up resistor):

Подключение кнопки с подтягивающим резистором
Теперь, когда кнопка разомкнута, пин Ардуино заземлён через резистор и защищён от непредвиденных помех.
Нужно также упомянуть, что схему можно собрать "наоборот" - подтянуть пин к высокому уровню, а при нажатии кнопки сажать его на землю. Такой вариант инвертированной логики мы как раз увидим чуть ниже.
Внутренняя подтяжка
На некоторых платах Ардуино есть встроенные подтягивающие резисторы номиналом около 20-50 кОм, которые можно активировать программно. Делается это через объявления режима INPUT_PULLUP для пина в коде скетча:
// включаем для пина №2 внутренний подтягивающий резистор
pinMode(2, INPUT_PULLUP);
Внутренняя подтяжка происходит к высокому уровню. Поэтому схему нужно собрать вот так, подсоединяя второй вывод кнопки к земле:

Схема подключения кнопки к Ардуино с использованием внутренней подтяжки пина к высокому уровню.
Простейший скетч: нажатие и отпускание
Теперь напишем скетч для этой схемы. Логика очень простая: при нажатии на кнопку будем зажигать встроенный светодиод, при отпускании кнопки - гасить его.
// номер пина, к которому подключена кнопка
const int buttonPin = 2;
// переменная для хранения состояния кнопки
int buttonState = 0;
void setup() {
// настраиваем пин кнопки как вход с внутренним подтягивающим резистором
pinMode(buttonPin, INPUT_PULLUP);
// настраиваем пин встроенного светодиода как выход
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
// считываем состояние кнопки
buttonState = digitalRead(buttonPin);
if (buttonState == LOW) {
// кнопка нажата - включаем светодиод
digitalWrite(LED_BUILTIN, HIGH);
} else {
// кнопка отпущена - выключаем светодиод
digitalWrite(LED_BUILTIN, LOW);
}
}
Дребезг контактов: что это и почему мешает
Когда вы нажимаете или отпускаете кнопку, её контакты не замыкаются мгновенно. В течение нескольких миллисекунд они могут "подпрыгивать" - многократно замыкаться и размыкаться. Это явление называется дребезгом контактов (contact bounce).

Примерный график сигнала при дребезге контактов
На графике видно, что вместо одного чёткого перехода сигнал "скачет" между 0 и 1. Для Arduino это выглядит как серия быстрых нажатий и отпусканий, хотя вы нажали кнопку всего один раз.
Из-за дребезга программа может реагировать на одно нажатие как на несколько, что приводит к ошибкам: светодиод может мигать, счётчик увеличиваться на несколько единиц и т.д.
Программная фильтрация дребезга
Самый простой способ избавиться от дребезга — добавить небольшую задержку после обнаружения нажатия или отпускания кнопки. Такой подход называется программной фильтрацией дребезга (debounce).
Давайте возьмём наш предыдущий скетч, и добавим повторную проверку состояния кнопки через некоторую паузу.
// пример ПЛОХОГО КОДА!
// не используйте в своих проектах!
// номер пина, к которому подключена кнопка
const int buttonPin = 2;
// переменная для хранения состояния кнопки
int buttonState = 0;
// задержка для фильтрации дребезга, в милисекундах
const unsigned long debounceDelay = 20;
void setup() {
// настраиваем режимы пинов
pinMode(buttonPin, INPUT);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
// считываем состояние кнопки
buttonState = digitalRead(buttonPin);
if (buttonState == LOW) {
// ждём немного, чтобы убедиться, что кнопка нажата
delay(debounceDelay);
// проверяем ещё раз состояние кнопки
buttonState = digitalRead(buttonPin);
if (buttonState == LOW) {
// только теперь включаем светодиод
digitalWrite(LED_BUILTIN, HIGH);
}
} else {
// кнопка отпущена - выключаем светодиод
digitalWrite(LED_BUILTIN, LOW);
}
}
У этого кода, как минимум, два недостатка:
- Во время выполнения инструкции delay основной цикл блокируется; никакие другие задачи, которые могли бы выполняться параллельно, не обрабатываются.
- После отпускания кнопки светодиод погаснет не сразу; вероятнее всего, отпускание произойдёт где-то на протяжении выполнения delay; программа будет ждать до конца delay и только на следующем цикле выключит светодиод.
Поэтому правильным является другой подход. При каждом изменении состояния кнопки мы запоминаем этот момент в переменной. И продолжаем заниматься другими делами, не блокируя основной цикл. Каждое новое изменение состояния начинает новый отсчёт. Но, если мы видим, что состояние продержалось неизменным в течении debounceDelay мс, то фиксируем нажатие или отпускание кнопки.
Возможных реализаций может быть много, вот один из примеров корректного кода:
// номер пина кнопки и светодиода
const int buttonPin = 2;
const int ledPin = 13;
int buttonState = 0; // подтверждённое состояние кнопки
int lastButtonState = 0; // состояние кнопки в предыдущем цикле
unsigned long lastDebounceTime = 0; // время последнего изменения состояния
unsigned long debounceDelay = 50; // задержка для фильтрации (мс)
void setup() {
pinMode(buttonPin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
}
void loop() {
int reading = digitalRead(buttonPin);
// если состояние изменилось — сбрасываем таймер
if (reading != lastButtonState) {
lastDebounceTime = millis();
}
// если прошло достаточно времени, считаем состояние стабильным
if ((millis() - lastDebounceTime) > debounceDelay) {
// если состояние изменилось — обновляем и реагируем
if (reading != buttonState) {
buttonState = reading;
if (buttonState == LOW) {
digitalWrite(ledPin, HIGH); // кнопка нажата — включаем светодиод
} else {
digitalWrite(ledPin, LOW); // кнопка отпущена — выключаем светодиод
}
}
}
lastButtonState = reading;
// ... тут можно выполнять другую полезную работу,
// цикл не блокируется расположенным выше кодом ...
}
В этом примере кнопка считается "нажатой" только если её состояние стабильно не меняется в течение 50 мс. Это значение можно подобрать экспериментально — обычно достаточно 20–50 мс.
Аппаратная фильтрация (RC-цепочка)
Для более надёжной фильтрации дребезга можно использовать аппаратный способ — RC-цепочку (резистор + конденсатор). Такая схема сглаживает резкие колебания сигнала при нажатии кнопки.

Схема подключения RC-цепочки для устранения дребезга контактов
Что мы здесь видим. По-прежнему в схеме остаётся резистор подтяжки 20 кОм. В данном примере подтягиваем к высокому уровню. А вот сигнал с кнопки идёт не напрямую на пин Ардуино, а через интегрирующую RC-цепь. Более подробно эта тема рассмотрена в статье про свойства RC-цепи. Здесь же упомянем, что интегрирующая RC-цепь устраняет из сигнала высокие частоты - а это как раз и есть мешающий нам дребезг.
Номиналы деталей следует выбрать так, чтобы постоянная времени RC-цепи превышала период дребезга. В данном примере (резистор 10 кОм и конденстор 0.1 мкФ) постоянная времени будет равна 1 мс. При необходимости следует увеличить сопротивление резистора и/или ёмкость конденсатора.
Аппаратная фильтрация особенно полезна, если требуется высокая надёжность или если кнопка подключена к прерыванию (interrupt). В особо ответственных схемах RC-цепь используют в сочетании с триггером Шмитта, что даёт чёткий крутой фронт сигнала от кнопки.
Библиотеки для кнопок: краткий обзор
Для сложных проектов, где требуется надёжная обработка нажатий, длинных и коротких кликов, удержания и др., удобно использовать специальные библиотеки для работы с кнопками. Они берут на себя фильтрацию дребезга и обработку событий.
| Библиотека | Особенности | Пример использования |
|---|---|---|
| Bounce2 | Простая фильтрация дребезга, минимальный код |
|
| OneButton | Обработка коротких/длинных нажатий, двойных кликов |
|
| EasyButton | Удобный интерфейс, поддержка событий |
|
Если у вас простая задача — достаточно программной фильтрации. Если требуется сложная логика (удержание, двойной клик, несколько кнопок) — используйте библиотеку.




Добавить комментарий