Управление кнопками на Ардуино

buttonsВ предыдущей статье я писал об организации индикации на светодиодах.

О работе с кнопками на Ардуино написано очень много. В этой статье я напишу свое видение, как удобно разрабатывать управление микроконтроллера с использованием тактовых кнопок, подключенных к цифровым входам.

Не буду вдаваться в подробности схем подключения кнопок к цифровым (и не только) входам микроконтроллера. В данной статье рассматривается кнопка, подтянутая резистором к плюсу и замыкающая вход на землю.  Поэтому состояния нажатой кнопки LOW, а отпущенной HIGH.

Тип кнопки не имеет значения, например может быть таким

Схема1

Самый простой скетч работы с кнопкой выглядит так

Оператор delay(1000) задает задержку между нажатиями кнопки. Если время нажатия превысит 1000 мс, то происходит автоматическое повторное нажатие кнопки. Такой метод может разве что сгодиться для отладки, но в реальной жизни мало применим, так как delay не дает выполнятся остальным операторам скетча (если только не используются прерывания) и ограничивает минимальное время между нажатиями кнопки.

В следующем скетче попробую запоминать состояния кнопки

Все вроде бы работает, но иногда проскакивает два или более срабатывания. В чем же дело? Дело в дребезге контактов механической кнопки.

Обычное срабатывание кнопки выглядит так. Казалось бы все правильно.

Нажатие кнопки

Но иногда срабатывание может выглядеть и так

Деребезг контактов при нажатии кнопки

Или даже так

Дребезг контактов кнопки

Особенно это заметно у изношенных или просто некачественных кнопок. Есть разные способы борьбы с этим эффектом, от установки конденсатора на цифровой вывод до целых электрических схем. Но у нас ведь есть целый микроконтроллер! Проанализировав работу кнопок, можно сделать вывод, что нажатие кнопки обычно длится более 150 мс, а дребезг контактов порождает импульсы длительностью 50 мс, ну очень редко чуть больше. Поэтому установив минимальное время между нажатиями кнопки в 50-100мс можно побороть эффект дребезга контактов «программным путем».

Иногда для уменьшения числа кнопок в устройстве применяют короткое и длинное нажатие на кнопку. В этом случае короткое нажатие фиксируется при отпускания кнопки А длинное нажатие при достижении заданного интервала. код получается такой.

Иногда, например в часах для установки времени, применяется режим, когда длинное нажатие кнопки вызывает срабатывание с заданным интервалом. Для реализации этой функции будет такой код.

Все это хорошо и функционально, но очень уж громоздко. Поэтому я убрал работу с кнопками в класс.

В конструкторе класса указывается цифровой вход кнопки. А также четыре необязательных параметра:

  • Таймаут для игнорирования дребезга контактов (По умолчанию 50 мс )
  • Время длинного нажатия кнопки, мс. Если 0, то длинное нажатие не фиксируется. (По умолчанию отключено)
  • Время удержания кнопки, после которого происходит автонажатие кнопки. Если 0, то не происходит. (По умолчанию отключено)
  • Интервал срабатывания кнопки при автонажатии (По умолчанию 500 мс)

Функция begin() производит инициализацию цифрового входа.

Функция loop() вызывается в основном цикле или по таймеру, но достаточно часто и возвращает следующие значения:

  • SB_NONE — ничего не произошло
  • SB_CLICK — событие срабатывания кнопки
  • SB_AUTO_CLICK — событие автонажатия кнопки при длинном удержании
  • SB_LONG_CLICK — событие длинного нажатия кнопки
Скачать класс для работы с кнопками с примером

Пример работы с данным классом выглядит так:

Можно использовать для фиксации событий кнопки таймер при помощи библиотеки TimerOne, о которой я писал в предыдущей статье

Данный класс можно адаптировать для работы с «аналоговыми кнопками» (несколько кнопок подключенных к аналоговому входу через делитель с разными номиналами сопротивлений).

Спасибо всем кто осилил )))

c112

 

Класс для работы с кнопками
2.3 KiB
4342 Downloads
Детали
Вы можете оставить отзыв или трекбек со своего сайта.

55 комментариев на «Управление кнопками на Ардуино»

  1. Марат:

    Алексей, спасибо за статью по кнопкам. Я не совсем понял — что за параметры передаются в класс. 4 — пин дуины. 50 и 500 тайминги короткого и длинного нажатий. А остальное (2000 и 4000) что такое?
    Спасибо.

    • Alexey Alexey:

      /**
      * Конструктор класса кнопки
      * Кнопка это цифровой пин подтянутый к питанию и замыкаемый на землю
      * Событие срабатывания происходит по нажатию кнопки (возвращается 1)
      * и отпусканию кнопки (возвращается время нажатия кнопки, мсек)
      * tm1 — таймаут дребезга контактов. По умолчанию 50мс
      * tm2 — время длинного нажатия клавиши. По умолчанию 2000мс
      * tm3 — врямы перевода кнопки в генерацию серии нажатий. По умолсанию отключено
      * tm4 — время между кликами в серии. По умолчанию 500 мс. Если tm3 = 0 то не работает
      */
      SButton::SButton(uint8_t pin,uint16_t tm1, uint16_t tm2,uint16_t tm3, uint16_t tm4){

      • Максим:

        Уважаемый Алексей ! а как замутить код короткого и длинного нажатия двух кнопок ик пульта для управления лед диммером ? тоесть первую коротко нажали включение 255 вторую коротко нажали 0 первая длинное удерживание яркость + аналогично вторая яркость — . За ранее спасибо !

      • rus309:

        Alex,добрый день.

        Сможете помочь ( не бесплатно)? Нужен скетч на ардуино:
        подключение к резистивной клавиатуре,
        при нажатии на определенные кнопки должен появляться минус на выводах ардуино (4 вывода)
        при длительном нажатии — длительный минус на выходе
        при коротком нажатии — короткий минус.
        в скетче в идеале возможность менять значение напряжения для подстройки по месту.

  2. Марат:

    Извиняюсь. Правильночитание у меня хромает. Оно хорошее, но почему-то хромает. (С)

  3. Otto:

    Большое спасибо за полезную и качественную + подробную статью!)))) Очень полезно!

  4. Виктор:

    Алексей, спасибо за статьи, эту и другие. Как задать параметры tm2, tm3, tm4 если мне не нужно у кнопки использовать длинное нажатие и серии нажатий?
    И можно ли использовать этот класс для реализации кнопки включения/выключения (длинное нажатие — прибор выключается, короткое — включается).

    • Alexey Alexey:

      Если не нужно длинное нажатие и автоповторение, то tm2-tm4 = 0
      Для включения выключения класс использовать можно, но при этом придется вручную задавать режим сна при длинном нажатии и в этом режиме кнопку посадить на прерывание, чтобы из режима сна выходить. Иначе выключение не имеет смысла — потребление контроллера будет большим

  5. Сергей:

    Спасибо огромное автору за статью.Респект.

  6. Иван:

    Алексей, огромное спасибо за статью, за «Класс для работы с кнопками». Мне, как новичку это очень помогло, взял на вооружение и буду пользовать с благодарностью. Кнопочки работают просто шик, но нехватка свободных пинов…, вынуждает обратиться с просьбой о помощи, адаптировать данный класс для работы с «аналоговыми кнопками». Перекопал кучу материала, но из-за нехватки опыта, знаний а может быть уже и возраста одолеть эту задачку самостоятельно не удается.

  7. Сергей:

    Хорошо. Научились нажимать кнопку и отправлять её эхо на комп. Как теперь прикрутить светодиод на эту кнопку? К примеру кнопка на D2 светик на D3
    Я только начал разбираться в этой теме.

  8. Алексей:

    Большое спасибо за статью. Пересмотрел кучу примеров, но Ваши примеры оказались наиболее легки для понимания. Строю управление светом в доме:
    плавное вкл.\выкл.
    case SB_AUTO_CLICK:
    if (brightness == 0 || brightness == 255) {
    fadeAmount = -fadeAmount ;
    }
    Двойной клик в Ваш класс добавить возможно? Для записи в eeprom максимальной яркости светодиода.

  9. Nick:

    Спасибо! очень хороший пример! Но не могу сообразить как? «Данный класс можно адаптировать для работы с «аналоговыми кнопками» (несколько кнопок подключенных к аналоговому входу через делитель с разными номиналами сопротивлений).»
    Если не трудно прдскажите куда копать?

  10. Kairat:

    Здравствуйте, Алексей. Я, к сожалению, никогда не сталкивался с ардуино. Но мне очень нужно подключить три кнопки к ардуино про мини, для управления компьютером ( например: Предыдущий трек, следующий трек и пауза и играть). Я могу воспользоваться Вашим классом и схемой подключения кнопок? Спасибо

  11. Евгений:

    Здравствуйте, Алексей! Очень удобная библиотека, спасибо. Попробовал ее применить, но столкнулся с неприятной вещью:
    «Long press button 1» посылается в Монитор последовательного порта еще до отпускания кнопки. Поэтому при удержании кнопки более 4 секунд, сначала передается сообщение «Long press button 1», а уже потом «Auto press button 1». Т.е. передается паразитное сообщение. Может это как-то можно исправить?

  12. Евгений:

    Разобрался. Видимо, для использования Auto press button нужно отключать Long press button.

  13. Лиля:

    Всем большой ПРИВЕТ! Помогите вставить две кнопки,нажатие на первую запускает движение в одну сторону и потом мотор просто останавливается,а нажатие на вторую кнопку запускает процесс в обратную сторону.Если нажимать одну и туже кнопку то мотор каждый раз начинает движение в одну сторону.

    #include

    #define HALFSTEP 8

    // Определение пинов для управления двигателем

    #define motorPin1 3 // IN1 на 1-м драйвере ULN2003

    #define motorPin2 4 // IN2 на 1-м драйвере ULN2003

    #define motorPin3 5 // IN3 на 1-м драйвере ULN2003

    #define motorPin4 6 // IN4 на 1-м драйвере ULN2003

    // Инициализируемся с последовательностью выводов IN1-IN3-IN2-IN4

    // для использования AccelStepper с 28BYJ-48

    AccelStepper stepper1 (HALFSTEP, motorPin1, motorPin3, motorPin2, motorPin4);

    void setup (){ stepper1.setMaxSpeed (800.0);

    stepper1.setAcceleration (100.0);

    stepper1.setSpeed (200);

    stepper1.moveTo (20000);

    } void loop (){

    // Изменяем направление, если шаговик достигает заданного положения

    if (stepper1.distanceToGo ()==0)

    stepper1.moveTo (-stepper1.currentPosition ());

    stepper1.run (); }

  14. Лиля:

    в этом скетче мотор не останавливаясь крутится то в одну то в другую сторону медленно разгоняясь и медленно останавливаясь.

  15. Цитата: «Данный класс можно адаптировать для работы с «аналоговыми кнопками»»
    Можно пример сдвумя кнопками, плиз?

  16. ЖЕНЯ:

    Нужно чтобы при нажатии одной кнопки каждый раз выводились разные сообщения на дисплее с помощью lcd.print(). У кого есть соображения?

  17. ЖЕНЯ:

    Проблема в том что функция void loop() не даст этого сделать так как имеет замкнутый цикл, и для того чтобы выводить разные сообщения при нажатии одной кнопки нужно воспользоваться операцией сравнения true и false. Тогда возможно что через каждые 3 секунды будет высвечиваться новое сообщение, а через 5 при отпускании кнопки остановится на оном. Вероятно что придется воспользоваться внутренней памятью ядра EEPROM так как собственной памяти для хранения данных в цикле void loop() нет.

  18. Серж:

    Как сделать чтобы после Auto press key срабатывала кнопка «отпускание»

  19. Иван:

    Помогите пожалуйста, как сделать что бы время нажатия кноки просто выводилось в монитор портов?

    • Валерий:

      Есть такая функция millis(). При помощи ее запросто можно сосчитать сколько кнопка была нажата.Для этого нужно запомнить значение millis() в момент нажатия, и значение millis() во время отпускания кнопки. Разность этих значений и будет время нажатия кнопки в миллисекундах.

  20. МУЖЧИНЫ кто согласится написать скетч за вознаграждение,к примеру на телефон.

  21. Валерий:

    Просто супер. Молодец Алексей. Спасибо тебе от всей души. Я целый день этот велосипед изобретал, но как-то не совсем заработало. )))

  22. Иван:

    Алексей, а можно добавить опцию двойного клика ?

    • Alexey Alexey:

      У меня в одном скетче есть реализация подсчета числа кликов за единицу времени. Можно прикрутить хоть двойной, хоть тройной. Постараюсь код оформить для нормального использования

  23. Влад:

    Большое спасибо. Отличная работа. Для себя добавил начальное состояние кнопки. у меня кнопка по умолчанию к земле прижата.
    Все работает в лучшем виде!

  24. Петр:

    Спасибо! Часто использую! Подскажите, как пин аналогового входа обзывать в библе? Ide Ардуино. Скажем, SButton button1(А4, 50, 0, 0, 0) — выдает ошибку

    • Alexey Alexey:

      Для Arduino UNO/ NANO/ PRO MINI
      A0 — 14
      A1 — 15
      A2 — 16
      A3 — 17
      A4 — 18
      A5 — 19

      • Александр:

        Добрый день Алексей все понравилось хоть много не понял. Я в свое время заказывал скетч по управлению мышки и использовал его на arduino ATmega32U4 скетч написан был прям для чайников заполняйте алгоритм движения в массивах заливаешь и мышки выполняет алгоритм и не чего не надо паять долгое время пользовался но вот возникла проблема как раз по вашей теме, нажатие левой клавиши я могу вызывать а вот удержание по времени нет. Может вы бы могли прочитать посмотреть скетч до работать к сожалению того кто его писал уже не найти координаты утеряны сам этими навыками не владею.буду рад услышать ваши условия заранее спс

  25. Игорь:

    Есть часы на DS3231 и 7-ми сегментниках.
    Хочется сделать удобную ручную 2-х кнопочную настройку следующей схемы:

    кн1 — режим установки ЧАСА кн2 — изменение значения
    кн1 — режим установки МИНУТ кн2 — изменение значения
    кн1 — режим отображения значения СЕКУНД кн2 — сброс значения секунд на 00 (возможность многократного нажатия)
    кн1 — выход из режима установки.

    Вот бы такой кусочек кода…

  26. Иван:

    Доброго времени суток.
    Новичок, осваиваюсь в программировании.
    Подскажите пожалуйста как убрать подтяжку, а то у меня готовые кнопки из наборы.
    Спасибо

  27. Вад:

    Спасибо! Вопрос: пин 2. Если разрешить прерывание, то оно также по кнопке отработается?

  28. Алексей:

    Большое спасибо за статью! Очень пригодилось для моего проекта.

  29. Dimarik:

    Автор, огромное спасибо за подсказку!!!
    Очень помогла херота с длительностью нажатия кнопок!
    Ненавижу С и подобное говно и вообще программировать, а ты просто зарешал!!!
    Признателен браток!

  30. Юсуф:

    Всем привет и хорошего настроения. Помогите пожалуйста мне написать простой скетч, защита от двойного включения кнопок.
    На примере:
    Если первая нажата кн 1 то она блокирует нажатие кн2
    Или первая нажата кн 2 то она блокирует нажатие кн1
    Или другой вариант :
    Нажимаем кн 1 горит светодиод 1
    (отпускаем не горит)
    Нажата кн2 горит светодиод 2
    (отпускаем не горит)
    При случайном нажатии двух кнопок оба светодиода потухают.
    (пример защиты кнопок от двойного включения на кранбалках или каких-то реверсах)

  31. Юсуф:

    int BUTTON_A=2;
    int LED_A=4;
    int BUTTON_B=3;
    int LED_B=5;

    void setup () {
    pinMode(LED_A,OUTPUT);
    pinMode(BUTTON_A,INPUT_PULLUP);
    pinMode(LED_B,OUTPUT);
    pinMode(BUTTON_B,INPUT_PULLUP);
    }
    void loop () {
    if(digitalRead(BUTTON_A)==HIGH){
    digitalWrite(LED_A,LOW);
    }else{
    digitalWrite(LED_A,HIGH);
    }
    if(digitalRead(BUTTON_B)==HIGH){
    digitalWrite(LED_B,LOW);
    }else{
    digitalWrite(LED_B,HIGH);
    }
    }
    Как можно сделать в этом скетче,чтобы кнопки BUTTON_A и BUTTON_B не могли работать одновременно, а блокировали друг друга.
    Подскажите пожалуйста кто сможет, весь интернет перерыл подходяшего примера даже близко нент.
    Везде только примеры
    Включить, выключить, клики, выдержки,счётчики и тд.

  32. Юсуф:

    int ledPin1=4;
    int ledPin2=5;
    int buttonPin1=2;
    int buttonPin2=3;
    void setup () {
    pinMode(buttonPin1,INPUT_PULLUP);
    pinMode(buttonPin2,INPUT_PULLUP);
    pinMode(ledPin1,OUTPUT);
    pinMode(ledPin2,OUTPUT);
    }
    void loop () {
    int pin1State=digitalRead(buttonPin1);
    int pin2State=digitalRead(buttonPin2);

    if(pin1State==LOW && pin2State==HIGH) {
    digitalWrite(ledPin1,HIGH);
    }else{
    digitalWrite(ledPin1,LOW);

    if(pin2State==LOW && pin1State==HIGH) {
    digitalWrite(ledPin2,HIGH);
    }else{
    digitalWrite(ledPin2,LOW);

    Три дня учился мучился но получилось. Вот этот скетч тоже заработал.
    (при нажатии двух кнопок оба светодиода потухают)

  33. Юсуф:

    int ledPin1=4;
    int ledPin2=5;
    int buttonPin1=2;
    int buttonPin2=3;
    void setup () {
    pinMode(buttonPin1,INPUT_PULLUP);
    pinMode(buttonPin2,INPUT_PULLUP);
    pinMode(ledPin1,OUTPUT);
    pinMode(ledPin2,OUTPUT);
    }
    void loop () {
    int pin1State=digitalRead(buttonPin1);
    int pin2State=digitalRead(buttonPin2);

    if(pin1State==LOW && pin2State==HIGH) {
    digitalWrite(ledPin1,HIGH);
    }else{
    digitalWrite(ledPin1,LOW);
    }

    if(pin2State==LOW && pin1State==HIGH) {
    digitalWrite(ledPin2,HIGH);
    }else{
    digitalWrite(ledPin2,LOW);
    }
    }

    (при нажатии двух кнопок оба светодиода потухают)

  34. Nizam:

    Добрый день. Могу ли я попросить у Вас помочь мне?. Нужно управлять 4-мя реле От кнопок без фиксации.Для каждого канала свое реле и кнопка.При нажатии на кнопку №1 включается реле№1 и остается включенным. При нажатии на одну из других кнопок отключается реле№1 и включается соответствующий реле.

  35. Александр:

    Спасибо Вам большое!

  36. Vito:

    Всем доброго дня! Помогите пожалуйста написать скетч :

    Светодиод зажигается если держать нажатой кнопку в течение 1 секунды.
    Чтобы погасить светодиод нужно нажать кнопку кратковременно (без
    использования функции delay()).

  37. Юрий:

    Самая классная библиотека управления кнопками !!
    Перепробовал многие, но хорошее (четкое, однозначное) срабатывание только с этой библиотекой.
    Как отблагодарить? 😉

  38. Сергей:

    Alexey, вы пишите, что ваш скетч возможно адаптировать под аналогово-резестивную клавиатуру. подскажите как это возможно, моих познаний не хватает

Ваш отзыв

Вы должны войти, чтобы оставлять комментарии.