Posted by lex232 on

Контроль доступа. Часть 4 — алгоритм ввода чисел с клавиатуры.

Контроль доступа. Часть 4 — алгоритм ввода чисел с клавиатуры.

В прошлой статье мы научились работать с кнопкой и управлять замком и светодиодом. Чтобы не мешать этому коду, сканирование клавиатуры мы временно закомментировали. Сегодня мы немного доработаем код, чтобы всё это не конфликтовало друг с другом, а самое главное, реализуем программу считывания цифрового кода в 4 значную переменную. Если она совпадёт, с числом-кодом, что записан в постоянной памяти микроконтроллера, дверь также откроется.

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

Serial.print(keyboardValue[r-1][c-1]);

Сам массив у нас был двумерный, и выглядел так

const char keyboardValue[4][3]          //Создаём двумерный массив
{
  {'1', '2', '3'},                      //структура клавиатуры
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'*', '0', '#'}
};

Теперь попробуем назначить значение массива, какой-нибудь переменной, и посмотрим, что будет. Пусть это будет переменная keyboardResult, типа unsigned char, или uint8_t, так как, у нас всегда с клавиатуры приходит положительное число, не более 9

keyboardResult = keyboardValue[r - 1];
Serial.print(keyboardResult);

Результат, если проверить, нас не порадует

Контроль доступа. Часть 4 — алгоритм ввода чисел с клавиатуры.

Вместо 1 — 49, вместо 2 — 50, и так далее. Кажется, есть какая-то закономерность, но почему числа уехали на 48 значений вверх? Все современные системы оперируют таблицей символов ASCii, давайте, взглянем на неё.

Контроль доступа. Часть 4 — алгоритм ввода чисел с клавиатуры.

Вот и ответ на первый вопрос. В таблице числовые значения, начинаются с кода 48, что равно «0». Именно поэтому, 1 у нас превратилась в 49. Но почему же тогда, в статье вывода данных с клавиатуры, команда выводила корректное значение?

Оказывается, char не такая-то и простая переменная. Название, явно произошло от слова character — что в переводе, может иметь значение символ. Отсюда следует некоторое исключение — все целочисленные типы, по умолчанию знаковые (signed или unsigned), но с char ситуация обстоит иначе, и стандарт описывает третий вариант, если не указан признак знака — тип для хранения базовых символов. Теперь попробуем от считанного значения в формате char, просто отнять 48, и посмотреть, что получится

{ 
 keyboardResult = keyboardValue[r - 1] - 48; // Отнимаем из массива 48, для приведения char к int
 Serial.println(keyboardResult);
}

Уже намного лучше, числовые значения обрабатываются корректно. Правда * и # всё равно превратились в какую-то чепуху, но с другой стороны, какая разница программе, что из себя представляет * и #. Это функциональные клавиши, которые будут делать сброс, подтверждение, задание нового пароля и другие функции. Кстати, наверное вы задумались, почему, всё же 250 и 243? Если взглянуть на таблицу ASCii выше, то можно увидеть, что код * и # = 42 и 35 соответственно. Что будет, если отнять от них 48? По идее, должны уйти в минус? Но ведь переменная у нас беззнаковая, т.е. от 0 до 255, и мы как-бы прыгаем в минус от 255. Так и получились значения 250 и 243 в терминале, математику не обманешь

Смотрите также:  Демо плата на AVR и глаза робота
Контроль доступа. Часть 4 — алгоритм ввода чисел с клавиатуры.

Теперь нам нужно эти нажатые цифры складывать в число. При каждой обработке нажатия кнопки — будем вызывать функцию void password_input (), где и будем вести обработку значений. Первым делом проверим, цифра это или символ, как видно выше звёздочка у нас 250, а решётка 243, значит отсеим эти значения.

if (keyboardResult >= 0 &amp;&amp; keyboardResult <= 9)

Сформируем переменную число нашего кода, в начале нашей программы

int password = 0;

Теперь, чтобы превратить нажатые клавиши в число, нужно просто умножать предыдущее значение на 10 (так мы увеличиваем разрядность предыдущей цифры) и прибавляем новое значение к этой переменной.

password = password * 10 + keyboardResult;

Код прост и понятен, соберём всё это в одну функцию, и сделаем отправку в консоль, для наглядности

void password_input ()
{
    if (keyboardResult >= 0 &amp;&amp; keyboardResult <= 9) // Обрабатываем цифры
    {
      password = password * 10 + keyboardResult;    // Прибавляем к значению password, новую нажатую клавишу, формируя число
      Serial.print("Password = ");
      Serial.println(password);                     //вывод на экран переменной password
    } else {
      //Здесь будем обрабатывать нажатие * и #
    }
}

Запускаем симуляцию, смотрим, что после каждой нажатой клавиши, формируется переменная password, которая содержит все нажатые до этого клавиши.

Смотрите также:  WG12864B вывел картинку
Контроль доступа. Часть 4 — алгоритм ввода чисел с клавиатуры.

Осталось сделать две важные функции — сброс, и подтверждение пароля. Сброс сделать легко — помним, что значение сброса у нас равно 250. Добавляем условие else if

else if (keyboardResult == 250)
  {
    password=0; // Cбрасываем пароль
    Serial.println("Reset_password");
  }

Где в теле условия просто приравниваем переменную password к нулю, и пишем для наглядности в терминал. Также я убрал из обработки нажатия клавиш строку терминала отдельно нажатой клавиши. Запускаем и проверяем

Контроль доступа. Часть 4 — алгоритм ввода чисел с клавиатуры.

Мы не будем ограничивать длину пароля 4 символами, да и вообще не будем никак ограничивать. Пароль будем подтверждать кнопкой #. Для этого сделаем ещё одно условие else if, и впишем оттуда переход на функцию check_password

  else if (keyboardResult == 243)  // Если нажата #
  {
    check_password();              // Переходим на процедуру проверки пароля
  }

Конечно, наш пароль должен храниться в энергонезависимой памяти. Мы обязательно реализуем это в следующих статьях, а пока процедуру проверки проведём с некой переменной correctPassword.

int correctPassword = 5467;

Создать функцию проверки на данном этапе не составит труда. Нужно всего лишь сравнить две переменные, и запустить функцию открытия замка в случае корректного пароля. Не забываем, во всех случаях сбрасывать переменную пароля password = 0;

void check_password()                             // Процедура проверки пароля
{
  if (password == correctPassword)                // Если введённый пароль совпал с тем, что сохранён в памяти
  {
    lock_open();                                  // Запускаем процедуру открытия замка
    password = 0;                                 // Сбрасываем пароль
    Serial.println("Password_OK");                
  } else                                          // Во всех других случаях, если пароль не совпал
  {
    Serial.println("Password_FAIL");
    password = 0;                                 // Сбрасываем пароль
  }
}

Снова проверяем наш код

Смотрите также:  Покраска робота
Контроль доступа. Часть 4 — алгоритм ввода чисел с клавиатуры.

Проверка работает корректно. Но есть ещё один нюанс, о котором мы забыли. Если человек, начал вводить символы и ушёл, а другой подходит и вводит корректный пароль — он всё равно не заработает. Нужно по прошествии некоторого времени (пусть будет 3 секунды) — обнулять переменную password. Создадим функцию, которая будет делать проверку. Проверять нужно простой 3 секунды и значение. Если значение и так 0, сбрасывать смысла нет.

void reset_password()                                   // Функция сброса пароля, при простое
{
  if (millis() - resetPassTime > 3000 &amp;&amp; password > 0)  // Если пользователь начал вводить пароль, и прошло время более 3 секунд
  {
    password=0;                                         // Сбрасываем пароль
    Serial.println("Reset_password_3s");
  }
}

А вот сбрасывать переменную времени будем в обработке клавиш. Каждая нажатая кнопка должна сбрасывать простой 3 секунды. Добавим в функцию key_scan

resetPassTime = millis();    // Сбрасываем переменную времени сброса пароля

Запускаем симуляцию, и смотрим, что случается после прошествии 3 секунд с момента нажатия клавиши.

Контроль доступа. Часть 4 — алгоритм ввода чисел с клавиатуры.

Работает корректно, как обработка пароля, так и сброс спустя 3 секунды, если далее клавиши не нажимаются. Теперь мы можем не только обрабатывать клавиши с клавиатуры, а ещё считывать пароль и сравнивать его с корректным значением.

Ссылка на исходный код этой статьи

В следующей статье мы избавимся от мелких огрехов, у нас остался delay на 50мс, нет индикации запрещающего светодиода, и если зажать кнопку на клавиатуре, программа сканирования крутиться в бесконечном цикле. Всё это исправим, добавим buzzer и соберём проект в реальном железе, посмотрим, как всё работает