Содержание
Кодовый замок на Arduino можно приспособить для различных целей. Это могут быть двери, шкатулки, сейфы или запуск какого-либо действия, например, запуск ракеты).
Техническое задание
Разработать кодовый замок на Arduino, который управляет электромагнитным реле. При правильном вводе 5-значного кода, срабатывает реле и загорается зеленый светодиод. Через 5 секунд реле приходит в изначальное состояние и зеленый светодиод гаснет. Если код введен неверно, то загорается красный светодиод в течение 5 секунд. Код можно вводить бесконечное количество раз.
Разработка
Давайте для начала смоделируем схему в Proteus
На схеме мы видим матрицу из кнопок, два светодиода и вместо катушки реле для удобства взят спикер, который при эмуляции начинает трещать. При правильном наборе кода загорается светодиод L_1 и трещит спикер LS1 в течение 5 секунд.
Код программы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
#include <Keypad.h> #define LED1 10 // красный светодиод #define LED2 11 // зеленый светодиод #define RELAY 12 // реле на замок #define NUM_KEYS 5 // количество знаков в коде char key; char myarraw[NUM_KEYS] = { '1', '2', '3', '4', '5'}; // массив с верным кодом char button_pressed[NUM_KEYS]; //массив для хранения нажатых кнопок int k=0; // счетчик нажатий int s=0; // счетчик совпадений нажатых кнопок с верными const byte ROWS = 4; // количество строк в матрице клавиатуры const byte COLS = 4; // количество столбцов char keys[ROWS][COLS] = { // таблица соответствия кнопок символам {'1','2','3','A'}, {'4','5','6','B'}, {'7','8','9','C'}, {'*','0','#','D'}}; byte rowPins[ROWS] = {5, 4, 3, 2}; // пины подключенных строк byte colPins[COLS] = {9, 8, 7, 6}; // пины подключенных столбцов Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); // создаем объект клавиатуры для работы с ней void setup() { pinMode(LED1, OUTPUT); // красный светодиод pinMode(LED2, OUTPUT); // зеленый светодиод pinMode(RELAY, OUTPUT); // реле управления замком digitalWrite(RELAY, HIGH); // вход реле инверсный, поэтому его сразу включаем (?!) } void loop () { key = keypad.getKey(); // спрашиваем у клавиатуры, есть нажатая кнопка? if ( key != NO_KEY ) // если она все-таки есть { button_pressed [k] = key; //сохраняем эту кнопочку в массиве k = k + 1; // запоминаем сколько уже кнопок нажали if(k == NUM_KEYS) // если нажали нужное количество кнопок {for ( uint8_t i = 0; i < NUM_KEYS; i++) // пройдемся по всему массиву { if (button_pressed[i] == myarraw[i]) // и проверим нажатые кнопки с верным кодом {s = s + 1; // плюсуем счетчик совпадений } } if(s == NUM_KEYS) //если у нас все кнопки совпали с кодом, то включаем реле { digitalWrite (RELAY, LOW); // включили реле digitalWrite (LED2, HIGH); // зажгли зеленый светик (пользователь ввел верный код) delay (5000); // ждем 5 секунд пока горит светик зеленый и включено реле digitalWrite (RELAY, HIGH); // гасим реле digitalWrite (LED2, LOW); // гасим светик k=0; //сбрасываем счетчик нажатий нашей переменной s=0; // сбрасываем счетчик совпадений нашей переменной } else { // если не все кнопки совпали с верным кодом digitalWrite (LED1, HIGH); // включаем красный светик (пользователь ввел неверный код) delay (5000); // ждем 5 секунд digitalWrite (LED1, LOW); // гасим красн светик k=0; // обнуляем счетчики, чтобы начать все заново s=0; // } } } } |
Описание кода
1 |
#include <Keypad.h> |
Для того чтобы мы могли обрабатывать, нажатия клавиш на нашей клавиатуре, мы могли бы написать сами с нуля, библиотеку обработки, но это заняло бы много времени, и в данном случае, программируя на Ардуино, на языке высокого уровня, в этом нет необходимости. Достаточно только подключить готовую библиотеку, которая идет в комплекте библиотек с нашей Arduino IDE.
1 2 3 4 5 6 7 |
#define LED1 10 // красный светодиод #define LED2 11 // зеленый светодиод #define RELAY 12 // реле на замок #define NUM_KEYS 5 // количество знаков в коде |
В данном проекте, нам потребуется использовать три значения, которые мы будем использовать при написании нашего кода. Мы могли бы пойти стандартным путем и создать три переменные, присвоить им имена и значения, и затем просто использовать их. Но мы решили пойти немножко дальше, и показать, как можно еще более удобным способом решить данную задачу. Мы создаем 4 директивы, LED1, LED2 и RELAY, NUM_KEYS и присваиваем им постоянное значение, которое идет после названия директивы. После значения, точку с запятой, как мы привыкли, закрывать нашу строку, ставить не требуется.
1 2 3 4 5 |
char key; char myarraw[NUM_KEYS] = { '1', '2', '3', '4', '5'}; // массив с верным кодом char button_pressed[NUM_KEYS]; //массив для хранения нажатых кнопок |
Здесь мы знакомимся с новым типом массивов и переменных char, в котором помимо цифровых значений, могут храниться символьные, например буквы, и различные знаки. Итак, мы создаем массив myarraw, который содержит 5 знаков, (не забываем про создание директивы). В данный массив мы записываем 5 значений, которые содержатся в фигурных скобках. Они будут являться кодом, по которому будет открываться наш замок, их вы впоследствии сможете поменять на любые другие. Затем нам нужно создать еще один массив, также 5 знаков, в котором будут храниться значения, наших нажатых кнопок.
1 2 3 |
int k=0; // счетчик нажатий int s=0; // счетчик совпадений нажатых кнопок с верными |
Здесь мы объявляем две переменные, к и s, и присваиваем им значение 0. В первой из них у нас будет храниться количество нажатий, а во второй количество совпадений, кода для открытия замка, который мы задали ранее в массиве, с кодом набранным на клавиатуре.
1 2 3 |
const byte ROWS = 4; // количество строк в матрице клавиатуры const byte COLS = 4; // количество столбцов |
Здесь же, мы задаем 2 константы формата byte, в целях экономии памяти, нашего контроллера мы пользуемся форматом для хранения переменных byte, а не привычный многим формат int. В данном случае он будет избыточен, для наших задач.
1 2 3 4 5 6 7 8 9 |
char keys[ROWS][COLS] = { // таблица соответствия кнопок символам {'1','2','3','A'}, {'4','5','6','B'}, {'7','8','9','C'}, {'*','0','#','D'}}; |
Теперь же, нам нужно будет создать, таблицу соответствия, кнопок клавиатуры символам, которые будут сохраняться в наших массивах. Как мы видим, их расположение, совпадает с нанесенными значками на клавиатуре.
1 2 3 |
byte rowPins[ROWS] = {5, 4, 3, 2}; // пины подключенных строк byte colPins[COLS] = {9, 8, 7, 6}; // пины подключенных столбцов |
Ну а здесь, нам требуется создать два массива, по 4 знака каждый, соответственно по количеству строк и столбцов, и задать, к каким пинам ардуино они у нас будут подключены. Формат переменной, как и в прошлом случае, у нас выбран byte.
1 |
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); // создаем объект клавиатуры для работы с ней |
Здесь нужно остановиться подробнее: Библиотека Keypad, описывает класс работы с клавиатурой. Т.е. создает тип данных «клавиатура», у этого типа данных свои параметры, которые мы указываем в скобках. Типа то, что клавиатура 4*4, к каким пинам подключены строки, к каким столбцы, и таблицу соответствия кнопок нашим символам. Т.е. Keypad это тип данных, наподобие int или char. Затем мы пишем имя своей переменной (создаваемого объекта) keypad с параметрами этой переменной в скобках. Как будто присваиваем значение этой переменной. И дальше работаем как с переменной, у которой можно менять параметры.
У этого класса есть разные методы — функции, к которым мы можем обращаться, чтобы работать с объектом, а еще есть внутренние, закрытые функции, которые работают сами, и мы не можем к ним обращаться. Т.е. keypad.getKey() — метод к которому мы обращаемся, чтобы получить значение нажатой клавиши, но у конкретной клавиатуры keypad. Классы позволяют создать несколько объектов. Т.е. мы можем подключить к ардуино 100 клавиатур и создать 100 объектов этих клавиатур. И потом работать с каждой по отдельности. Типа keypad1.getKey(), keypad2.getKey()… В рамках данной статьи, подробно разбирать, работу с классами и объектами, я думаю будет излишним, те кто заинтересуется данным вопросом, думаю что сможет теперь самостоятельно, найти информацию поиском.
1 2 3 4 5 6 7 |
pinMode(LED1, OUTPUT); // красный светодиод pinMode(LED2, OUTPUT); // зеленый светодиод pinMode(RELAY, OUTPUT); // реле управления замком digitalWrite(RELAY, HIGH); // вход реле инверсный, поэтому его сразу включаем |
Ну, здесь все просто, как мы помним в начале программы, мы создавали директивы, которые вызываем здесь, и просто подставляем их значения, в данном случае, это будут номера пинов Ардуино. Тем самым, мы конфигурируем наши три указанные пина на выход, и затем один из них притягиваем по умолчанию к единице. Если с пинами все понятно, они будут зажигать и гасить наши светодиоды, и управлять реле, то выход 12 пина, мы устанавливаем равным логической единице, из за того, что наше реле, включается, при подаче логического нуля, на пин ардуино. А открытый по умолчанию замок, нам, разумеется не требуется.
1 |
{ key = keypad.getKey(); // спрашиваем у клавиатуры, есть нажатая кнопка? |
Здесь мы опрашиваем нашу клавиатуру, были ли нажаты какие либо кнопки на клавиатуре.
1 |
if ( key != NO_KEY ) // если она все-таки есть |
1 2 3 4 5 |
button_pressed [k] = key; //сохраняем эту кнопочку в массиве k = k + 1; // запоминаем сколько уже кнопок нажали if(k == NUM_KEYS) // если нажали нужное количество кнопок |
Если была нажата какая либо из кнопок, то выполняем действие, сохраняем кнопку в массиве button_pressed. Затем наш счетчик нажатий, после нажатия первой кнопки, мы увеличиваем на единицу. Если количество нажатий соответствует значению, нашей директиве NUM_KEYS, то есть 5, мы выполняем код, следуем дальше.
1 2 3 4 5 6 7 |
{for ( uint8_t i = 0; i < NUM_KEYS; i++) // пройдемся по всему массиву { if (button_pressed[i] == myarraw[i]) // и проверим нажатые кнопки с верным кодом {s = s + 1; // плюсуем счетчик совпадений |
Итак, в цикле for, мы присваем переменной uint8_t i, что означает INT 8 бит, значение 0. Затем проводим сравнение, если значение i меньше 5, мы увеличиваем на единицу значение i, и выполняем условие 5 раз. Сравниваем по символьно значения наших массивов, в котором хранятся набранные значения кнопок, и заданные нами в начале, для проверки на совпадение, на открытие замка. Если совпали значения, то счетчик совпадений, увеличиваем на единицу.
Для изменения кода доступа достаточно поменять значения цифр в строке 11 на свои цифры.
Сборка в железе
Для работы в железе нам понадобится собственно сама плата Arduino Uno
Матричная клавиатура 4х4
И модуль электромагнитного реле
А вот и видео работы:
Все файлы прикрепляю в этом архиве. Как заливать прошивку в Arduino и проводить эмуляцию в Proteus, можно прочитать в этой статье.
На Алике я находил прикольные замочки, которые можно навесить на вашу дверь
Их можно глянуть по этой ссылке.