Клавиатуры и кнопки на Arduino

Клавиатуры, используемые в проектах

Цифровая клавиатура слева. Аналоговая клавиатура справа.

Когда я приступил к сборке dropController и camController, подходящие навигационные клавиатуры найти не удавалось — доступные варианты оказывались либо слишком дорогими, либо не подходили по функциональности, и я решил сделать свои. Это были несложные клавиатуры со стандартной схемой подключения кнопок. Каждая кнопка была привязана к отдельному пину Arduino, то есть для 5 кнопок требовалось 5 пинов. Такой подход устраивал до тех пор, пока не возникла необходимость подключить дополнительные соленоидные клапаны — и тут выяснилось, что свободных пинов не осталось.

Я возобновил поиск готовых решений и обнаружил клавиатуру Keyes Keypad на Taobao. Они недорогие и компактнее тех, что я изготавливал самостоятельно. Вдобавок они задействуют лишь один пин — это аналоговые клавиатуры, работающие через единственный аналоговый вход Arduino.

Принципиальное различие между цифровой и аналоговой клавиатурой состоит в способе считывания нажатий и обработке полученного значения. У цифровой клавиатуры каждый пин/кнопку приходится опрашивать отдельно. К примеру: if left_key is pressed, if right_key is pressed. Аналоговая же клавиатура использует единственную переменную, значение которой меняется в зависимости от нажатой кнопки. К примеру: if keypress is left, if keypress is up. На мой взгляд, с таким подходом работать несколько проще, хотя писать код можно любым удобным способом.

Цифровая клавиатура

Цифровая навигационная клавиатура — передняя и задняя стороны

Схема подключения каждой отдельной кнопки достаточно проста. Присутствует провод GND с подтягивающим к земле резистором 10 кОм (pull-down), провод VCC/+5V и сигнальный провод, идущий к цифровому пину Arduino. Подтягивающий резистор обеспечивает состояние LOW, когда кнопка не нажата. При нажатии резистор 10 кОм шунтируется, и на выходе устанавливается состояние HIGH.

Схема подключения кнопки — вариант 1 Схема подключения кнопки — вариант 2

Обе приведённые схемы идентичны. Внутри кнопки левая и правая стороны электрически связаны между собой. Это хорошо видно по условному обозначению кнопок:

Схематическое обозначение кнопки — вариант 1 Схематическое обозначение кнопки — вариант 2

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

Клавиатура из нескольких кнопок

Ниже представлена используемая мной раскладка:

Раскладка цифровой клавиатуры

В сети имеется множество материалов по данной теме, а на официальном сайте Arduino есть хорошее введение: Arduino Button Tutorial

Для обнаружения нажатий клавиш я применяю библиотеку Button от Alexander Brevig. Она заметно упрощает код. В начале скетча задаётся, какие кнопки подключены к каким пинам и какой тип подтяжки используется — pull-down (к GND) или pull-up (к +5V). Я всегда применял pull-down.

Button ok_button = Button(2, PULLDOWN);
Button rt_button = Button(3, PULLDOWN);
Button dn_button = Button(4, PULLDOWN);
Button up_button = Button(5, PULLDOWN);
Button lf_button = Button(6, PULLDOWN);

Затем в основном теле скетча нажатия клавиш определяются одной строкой:

ok_button.uniquePress();

Функция возвращает TRUE или FALSE и может применяться различными способами. Например:

if ( ok_button.uniquePress() )  { Serial.Print( "OK button was pressed."); }

Библиотека позволяет фиксировать уникальные нажатия, текущее состояние кнопки (нажата или отпущена) и многое другое. Её можно найти на Arduino Playground: Button Library

Аналоговая клавиатура

Клавиатура Keyes Keypad

Аналоговая клавиатура формирует различное напряжение (аналоговое значение) в зависимости от того, какая кнопка нажата. Это значение считывается единственным аналоговым пином Arduino. Теоретически такой подход медленнее использования цифровых пинов, однако на практике разницы я не ощутил. К тому же аналоговая процедура, которую я использую (позаимствованная из интернета), обеспечивает более чёткий отклик по сравнению с той, что я применял для цифровой клавиатуры. С цифровым вариантом у меня по-прежнему наблюдались проблемы с дребезгом и ложными срабатываниями (хотя особых усилий по их устранению я не предпринимал).

Аналоговые клавиатуры я приобретаю на Taobao в Китае, но они есть и на eBay. Собрать подобную клавиатуру своими руками несложно — кнопки располагаются по схеме делителя напряжения (а точнее, при пяти кнопках — по схеме резисторной лестницы).

Самодельная аналоговая навигационная клавиатура на одном пине

Вот простая клавиатура, собранная на макетной плате. Сигнальный провод от кнопок подключён к пину A0 Arduino.

Самодельная клавиатура на одном проводе — схема 1 Самодельная клавиатура на одном проводе — схема 2

Здесь использованы резисторы на 2,2 кОм, поскольку они были в наличии, но подойдут и другие номиналы — главное, чтобы они были достаточно большими. Резисторы на 1 кОм и 2 кОм тоже вполне годятся.

Скетч для тестирования клавиатуры

Следующий скетч считывает значение с аналогового пина и выводит его в Serial Monitor.

/*
Keypad Tester 01. Created by Aiten Bexultan
*/

// Глобальные переменные
const byte keypadPin     = A0;
int val = 0;

void setup()
{
  Serial.begin(9600);
  while (!Serial)   {  ;    }
  Serial.println(F("Start\n"));
  // Настраиваем аналоговый пин на вход и включаем внутренний подтягивающий резистор
  pinMode(keypadPin, INPUT_PULLUP);
}

void loop()
{
    val = getKeyValue();
    Serial.println( val );
    delay(100);
}

int getKeyValue()
{
    int pinVal;
    pinVal = analogRead(keypadPin);
    return pinVal;
}

С резисторами на 2,2 кОм были получены следующие значения:

  • Ни одна кнопка не нажата = 1021–1023

  • Первая кнопка = 14 или 15

  • Вторая кнопка = 72 или 73

  • Третья = 124

  • Четвёртая = 170

  • Пятая = 212 или 213

Из-за разброса параметров компонентов ваши значения, вероятнее всего, будут несколько отличаться.

Активация подтягивающего резистора на аналоговом пине означает подачу на него напряжения +5V (фактически чуть меньше), поэтому без нажатия кнопок пин показывает значение около 1023.

Обратите внимание: нажатие первой кнопки напрямую замыкает A0 на GND. Подтягивающий резистор на пине предохраняет Arduino от короткого замыкания, а реальный ток при этом составляет лишь около 1 миллиампера.

Если перенести подключение GND на противоположный конец цепочки, значения кнопок поменяются местами: первый пин теперь будет давать 212, а пятый — 14.

Самодельная клавиатура — обратное подключение GND

В скетче можно напрямую использовать значение, возвращаемое analogRead(), однако более удобный подход — использовать метки направлений. Это позволяет писать код в стиле if (buttonPress==LEFT) {} или if (buttonPress==DOWN) {}.

/*
Keypad Tester 02. Created by Aiten Bexultan
*/

// Глобальные переменные
const byte NONE = 0;
const byte LEFT = 1;
const byte UP = 2;
const byte RIGHT = 3;
const byte DOWN = 4;
const byte SELECT = 5;

const byte keypadPin = A0;
byte key = 0;

void setup()
{
  Serial.begin(9600);
  while (!Serial)   {  ;    }
  Serial.println(F("Start\n"));
  pinMode(keypadPin, INPUT_PULLUP); // настраиваем аналоговый пин на вход
}

void loop()
{
    key = getKey();
    if (key==LEFT)    { Serial.println("LEFT"); }
    if (key==RIGHT)   { Serial.println("RIGHT"); }
    if (key==UP)      { Serial.println("UP"); }
    if (key==DOWN)    { Serial.println("DOWN"); }
    if (key==SELECT)  { Serial.println("SELECT"); }
    if (key==NONE)    { Serial.println("NO KEY PRESSED"); }
    delay(100);
}

/*
Значения клавиш:
Left / Первая кнопка = 15
Up / Вторая кнопка = 72
Down / Третья = 124
Right / Четвёртая = 170
Select / Пятая = 212
Ни одна не нажата = 1021

Из-за допусков компонентов и даже температуры
нужно проверять диапазон значений.
Я обычно использую +/- 20. Это даёт:
Left - 0 до 35
Up - 52 до 92
Down - 104 до 144
Right - 150 до 190
Select - 192 до 232
*/
byte getKey()
{
    int val = 0;
    byte button = 0;
    val = analogRead(keypadPin);

    button = NONE; // используем NONE как значение по умолчанию
    if ( val <= 35)  {  button = LEFT; }                                // влево
    else if ( (val >= 52)  &&  (val <=92)    )   {  button = UP;  }     // вверх
    else if ( (val >= 104) &&  (val <= 144 ) )   {  button = DOWN;  }   // вниз
    else if ( (val >= 150) &&  (val <= 190)  )   {  button = RIGHT;  }  // вправо
    else if ( (val >= 192) &&  ( val <=232)  )   {  button = SELECT;  } // выбор
    return button;
}

Запустите скетч и откройте Serial Monitor — вы увидите примерно следующее:

Serial Monitor — вывод клавиатуры

При нажатии каждой кнопки в Serial Monitor отображается её название. Если ни одна кнопка не нажата, выводится сообщение «NO KEY PRESSED». Если результат отличается от ожидаемого, попробуйте скорректировать диапазоны в функции getKey().