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



В этом проекте я расскажу, как сделать замок с отпечатком пальца на входную дверь.

Шаг 1: Материалы



Вот список необходимых материалов и инструментов.

Электроника:

  • Сканер отпечатков пальцев (и коннектор JST)
  • Набор LCD (c ATmega328)
  • ATtiny85
  • NPN-транзистор
  • Динамик-пищалка
  • Провод для спикера
  • Кейс (в шаге 9 будут файлы для 3Д-печати)
  • Медная плёнка
  • Регулятор напряжения 5V
  • Батарейка 9V
  • Коннектор для батарейки 9V
  • SPDT-выключатель

Для удобства я приложу готовый вишлист на сайте Sparkfun.

Инструмент:

  • Паяльник и припой
  • Изолента
  • Провода и джамперы
  • Кусачки / стриппер
  • Плата прототипирования
  • Разные резисторы
  • Винты
  • Дрель
  • Несколько светодиодов для тестирования
  • Плата FTDI 5V
  • Пистолет с горячим клеем
  • Доступ к 3Д-принтеру
  • Опционально: сокет для интегральных схем (8-пиновый для ATtiny и 28-пиновый для ATmega)
  • Опционально: еще одна плата Ардуино / конденсатор 10uF (подробности в шаге 5)

Шаг 2: Схема устройства







Приобретённый в Sparkfun LCD-набор шёл с ATmega328, управляющей дисплеем. ATmega328 достаточно мощна и может быть использована не только для управления дисплеем, но и для других задач. Ввиду этого мы можем использовать её вместо Ардуино для коммуникации со сканером отпечатков пальцев и отправки команд на ATtiny85, управления дисплеем и пищалкой.

Чтобы биометрический дверной замок не работал всё время, я встроил в него выключатель, срабатывающий в тот момент, когда кейс закрывается. Если кейс закрыт — питание на девайс не подается, и мы экономим ресурсы батарейки.

Важная заметка: Сканнер отпечатков пальцев работает при напряжении 3.3V, так что я рекомендую использовать разделитель напряжения, который будет преобразовывать сигналы от ATmega к 3.2V. Разделитель напряжения состоит из резистора на 560 Ом между D10 / вторым пином сканера и резистором на 1 КОм между GND / вторым пином сканера.

Распиновка LCD:

  • D10 — пин 1 сканера (черный провод)
  • D11 — пин 2 сканера (через разделитель напряжения)
  • D12 — ATtiny85
  • D13 — Пищалка

Распиновка ATtiny85:

  • Пин 5 (0 в коде программы) — вход с ATmega
  • Пин 3 (4 в коде программы) — транзистор / желтый светодиод
  • Пин 7 (2 в коде программы) — светодиод индикации

Заметка: к пину 5 на ATtiny рекомендуется подключить подтягивающий резистор

Шаг 3: Собираем компоненты из LCD-набора


Название шага говорит само за себя: handy-dandy quick start/assembly guide

Шаг 4: Собираем схему на плате прототипирования





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

После сборки я покрыл верх и низ платы горячим клеем — это закрепило и изолировало элементы схемы. Горячий клей не повредит микросхему.

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

Шаг 5: Программирование ATmega328


Как уже говорилось в шаге 2, у ATmega328 достаточно сильный процессор и достаточно пинов для управления LCD, в то время как он управляет другими дополнительными компонентами. Чтобы добиться этого, нужно запрограммировать чип.

Если у вас есть Arduino Uno или Duemilanove, вы можете просто снять с них чип и заменить его тем, который шел в наборе. Либо вы можете найти плату FTDI Basic Breakout (5V) и припаять насадки к её стороне (смотрите картинки в шаге 3)

Также вам нужно будет залить код в режиме «Duemilanove w/ ATmega328».

Код внизу — рабочая программа для проверки работоспособности девайса.

#include "LiquidCrystal.h"

LiquidCrystal lcd(2,3,4,5,6,7,8);

void setup() {
  pinMode(9, OUTPUT); //подсветка
  pinMode(13, OUTPUT); //пищалка
  
  lcd.begin(16, 2); //16 знаков в ширину, 2 в высоту
  
  digitalWrite(9, HIGH); //включаем подсветку
  
  lcd.print("  Hello world!  "); //центрируйте текст при помощи пробелов
  delay(2000);
}

void loop() { 
  //пищалка включается и выключается, её состояние отображается на дисплее
  lcd.clear();
  lcd.print("  Buzzer is on  ");
  tone(13, 262, 1000);
  delay(1000);
  lcd.clear();
  lcd.print(" Buzzer is off  ");
  delay(1000);
  }

Файлы

Шаг 6: Настраиваем сканер отпечатков пальцев


Для коммуникации со сканером я использовал эту библиотеку. Прямая ссылка на скачивание здесь.

Для проверки работоспособности кода загрузите эту программу проверки «миганием».

У сканера отпечатков есть своя встроенная память для хранения данных. Так что после того, как вы убедитесь, что сканер работает, загрузите эту программу, чтобы добавить ваш отпечаток в базу данных под id #0. Откройте последовательную консоль и просто следуйте инструкциям.

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

/* 
Этот простой код включит и выключит светодиод.
Он используется для того, чтобы понять, работает ли коммуникация.
 */

#include "FPS_GT511C3.h"
#include "SoftwareSerial.h"

//Настройка железа - сканер пальцев соединён с:
//цифровым пином 10(arduino rx, fps tx)
//цифроывм пином 11(arduino tx - резистор 560ohm fps tx - резистор 1000ohm - GND)
//это понижает 5v tx примерно до 3.2v и мы не сожжем наш сканер

FPS_GT511C3 fps(10, 11);

void setup(){
  Serial.begin(9600);
  fps.UseSerialDebug = true; // вы сможете увидеть сообщения на последовательном дебаг-экране
  fps.Open();
}

void loop(){
  // тест мигания светодиодов для сканера
  fps.SetLED(true); // включает LED внутри сканера
  delay(1000);
  fps.SetLED(false);// выключает LED внутри сканера
  delay(1000);
}

Программа регистрации данных в сканер

#include "FPS_GT511C3.h"
#include "SoftwareSerial.h"

//Настройка железа - сканер пальцев соединён с:
//цифровым пином 10(arduino rx, fps tx)
//цифроывм пином 11(arduino tx - резистор 560ohm fps tx - резистор 1000ohm - GND)
//это понижает 5v tx примерно до 3.2v и мы не сожжем наш сканер

FPS_GT511C3 fps(10, 11);

void setup(){
  Serial.begin(9600);
  delay(100);
  fps.Open();
  fps.SetLED(true);
  Enroll();
}

void Enroll(){
  // Тест регистрации
  // поиск открытого id
  int enrollid = 0;
  fps.EnrollStart(enrollid);

  // регистрация
  Serial.print("Press finger to Enroll #");
  Serial.println(enrollid);
  while(fps.IsPressFinger() == false) delay(100);
  bool bret = fps.CaptureFinger(true);
  int iret = 0;
  if (bret != false)
  {
    Serial.println("Remove finger");
    fps.Enroll1(); 
    while(fps.IsPressFinger() == true) delay(100);
    Serial.println("Press same finger again");
    while(fps.IsPressFinger() == false) delay(100);
    bret = fps.CaptureFinger(true);
    if (bret != false)
    {
      Serial.println("Remove finger");
      fps.Enroll2();
      while(fps.IsPressFinger() == true) delay(100);
      Serial.println("Press same finger yet again");
      while(fps.IsPressFinger() == false) delay(100);
      bret = fps.CaptureFinger(true);
      if (bret != false)
      {
        Serial.println("Remove finger");
        iret = fps.Enroll3();
        if (iret == 0)
        {
          Serial.println("Enrolling Successfull");
        }
        else
        {
          Serial.print("Enrolling Failed with error code:");
          Serial.println(iret);
        }
      }
      else Serial.println("Failed to capture third finger");
    }
    else Serial.println("Failed to capture second finger");
  }
  else Serial.println("Failed to capture first finger");
}

void loop(){
  delay(100000);
}

Файлы

Шаг 7: Программируем ATtiny85



ATtiny85 — это что-то типа дешевого Ардуино, собранного в одном чипе. ATtiny85 может быть запрограммирована другим Ардуино, включая ATmega328, который есть в нашем наборе LCD. В проекте он используется для запуска очень простых команд: проверить сигнал с ATmega и открыть ворота, если сигнал правильный.

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

После загрузки кода, пин 13 на Ардуино (встроенный светодиод) должен загореться, оповещая, что код загружен.

Итоговый код:

//Получает краткий сигнал от основного модуля для закрытия реле

void setup(){
  pinMode(2,OUTPUT); //LEd индикации через резистор на 10K
  pinMode(4,OUTPUT); //пин странзистора, открывающий гараж
  pinMode(0,INPUT); //ввод 
  delay(500); //даём девайсу время для старта
  digitalWrite(2, HIGH); //LED индикации
}

void loop(){

  if(digitalRead(0)){ //простой паттерн для переключения транзистора 
    delay(125);
    if(digitalRead(0)==false){ 
      delay(55); //ждём, так как таймер ATtiny не идеален
      if(digitalRead(0)){
        delay(55);
        if(digitalRead(0)==false){ 
          delay(55);
          if(digitalRead(0)){
            delay(55);
            if(digitalRead(0)==false){
              digitalWrite(4, HIGH); //транзистор "нажимает" кнопку
              delay(1000);
              digitalWrite(4,LOW);
              digitalWrite(2,LOW);
              delay(1000);
              digitalWrite(2, HIGH);
            }
          }
        }
      } 
    }
  }
}

Файлы

Шаг 8: Итоговый код

Ниже приложена программа для Ардуино, которую я написал с использованием библиотек сканера и дисплея. Чтобы было понятно, что происходит в каждой части программы, я постарался закомментировать всё наилучшим образом. После загрузки этого кода всё должно заработать и всё что останется сделать — интегрировать систему в дверь.

Предупреждение: если библиотека сканера не работает, то попробуйте использовать старую версию IDE Ардуино.

Код для ATmega238:

#include "LiquidCrystal.h" //библиотека дисплея
#include "FPS_GT511C3.h" //библиотека fps (сканера отпечатков)
#include "SoftwareSerial.h" //используется библиотекой сканера

//Настраиваем пины дисплея и сканера
LiquidCrystal lcd(2, 3, 4, 5, 6, 7, 8); //распиновка дисплея
FPS_GT511C3 fps(10, 11); //RX, TX

boolean isFinger = false; //true если библиотека fps засечет палец на сканере

//выходные пины
const int buzzerPin = 13;
const int backlightPin = 9;
const int attinyPin = 12;
const String idNames[] = 
{
  "self","Bro", "Ryan", "Mom", "Dad", "Auntie", "Grandma", "Zeide", "Person", "person", "Thumb"};

void setup(){
  //настраиваем выходы
  pinMode(buzzerPin, OUTPUT);
  pinMode(backlightPin, OUTPUT);
  pinMode(attinyPin, OUTPUT);

  //для отладки
  //Serial.begin(9600);
  fps.UseSerialDebug = false; //становится true для отладки fps через последовательный порт

  //инициализация библиотек
  lcd.begin(16,2);
  digitalWrite(backlightPin, HIGH); //подсветка LCD
  fps.Open();
  fps.SetLED(true); //светодиод на fps
  //звук загрузки
  for(int i=0; i<30; i++){
    tone(buzzerPin, 50+10*i, 30);
    delay(30);
  }
  tone(buzzerPin, 350);

  //вывод стартового сообщения
  lcd.print("Put your finger "); //команда вывода на экран
  lcd.setCursor(0, 1); //устанавливаем курсор на нулевую колонку первой строки
  lcd.print(" on the scanner ");
  delay(150);
  noTone(buzzerPin); //останавливаем стартовый звук

}
void loop(){
  //сканируем и распознаём отпечаток, когда приложен палец
  waitForFinger();

  lcd.clear(); //очищаем экран и устанавливаем курсов в положение 0,0
  fps.CaptureFinger(false); //захватываем отпечаток для идентификации
  int id = fps.Identify1_N(); //идентифицируем отпечаток и сохраняем id

  if(id <= 10){
    lcd.print(" Access granted "); //сообщение об успехе
    lcd.setCursor(0,1);

    //выводим на экран имя когда дверь открывается
    String message = "  Hey " + idNames[id] + "!";
    lcd.print(message);

    tone(buzzerPin, 262, 1000);
    delay(1500);

    //отправляем сигнал для открытия двери
    digitalWrite(attinyPin, HIGH); //первый импульс синхронизирует задержку (10ms)
    delay(5);
    digitalWrite(attinyPin, LOW);
    delay(3);
    digitalWrite(attinyPin, HIGH); //следующие два - открывают дверь
    delay(15);
    digitalWrite(attinyPin, LOW);
    delay(5);
    digitalWrite(attinyPin, HIGH);
    delay(10);
    digitalWrite(attinyPin, LOW);
    delay(1000);

    lcd.clear();
    lcd.print("Don't forget to ");
    lcd.setCursor(0,1);
    lcd.print("  shut me off!  ");
    delay(2000);

    waitForFinger(); //нажмите чтобы продолжить запись

    while(true){ //сохраняет новый отпечаток
      //выводит сообщение на экран
      lcd.clear();
      lcd.print(centerText("So you want to"));
      lcd.setCursor(0,1);
      lcd.print(centerText("scan a new one?"));
      delay(2000);

      //Скопировано и слегка модифицировано из примера регистрации данных:
      int enrollid = 11;

      //выбираете какой id переписатьсоздать
      //отпустите палец, когда хотите записать id/имя, напечатанное на экране

      waitForFinger(); //ждёт, когда будет нажат fps

      while(enrollid==11){
        for (int i = 1; i1){
            lcd.print(i);
            enrollid = i-1;
            break;
          }
        }
      }

      //предупреждение, если в данном слоте уже есть данные
      if(fps.CheckEnrolled(enrollid)){ 
        lcd.clear();
        lcd.print(" Warning! ID #");
        lcd.print(enrollid);
        lcd.setCursor(0,1);
        lcd.print(" has data. OK?  ");
        delay(2500);

        waitForFinger(); //ждёт, когда будет нажат fps

        fps.DeleteID(enrollid); //удаляет данные
        delay(100);
      }

      //Enroll
      fps.EnrollStart(enrollid);
      lcd.clear(); 
      lcd.print("Place finger to ");
      lcd.setCursor(0,1);
      lcd.print("enroll #");
      lcd.print(enrollid); //выводит id, который был добавлен
      waitForFinger(); //ждёт, когда будет нажат fps

      //захватывает отпечаток и сохраняет его в память трижды для точности данных
      bool bret = fps.CaptureFinger(true); //картинка высокого качества для записи
      int iret = 0; //в случае ошибки

      if (bret != false){ //первая регистрация
        lcd.clear();
        lcd.print(" Remove finger  ");
        fps.Enroll1();
        while(fps.IsPressFinger() == true) delay(100); //ждёт пока уберут палец
        lcd.clear();
        lcd.print("  Press again   ");
        waitForFinger(); //ждёт, когда будет нажат fps
        bret = fps.CaptureFinger(true);

        if (bret != false){ //вторая регистрация
          lcd.clear();
          lcd.print(" Remove finger  ");
          fps.Enroll2();
          while(fps.IsPressFinger() == true) delay(100);
          lcd.clear();
          lcd.print("Press yet again ");
          waitForFinger(); 
          bret = fps.CaptureFinger(true);

          if (bret != false){ //третья регистрация
            iret = fps.Enroll3();
            if (iret == 0){ //проверяет, были ли какие-нибудь ошибки
              lcd.clear();
              lcd.print("    Success!    ");
              delay(2000);
              beep(); //выключает Ардуино
            }
            else{ //запускает этот код в случае любой ошибки
              lcd.clear();
              lcd.print("Fail. Try again ");
              delay(1000);
            }
          }
          lcd.clear();
          lcd.print("   Failed 3rd   "); //ошибка на третьей записи
          delay(1000);
        }
        lcd.clear();
        lcd.print("   Failed 2nd   "); //ошибка на второй записи
        delay(1000);
      }
      lcd.clear();
      lcd.print("   Failed 1st   "); //ошибка на первой записи
      delay(1000);
    }
  }

  else{
    lcd.print("Fingerprint is"); //если отпечаток не распознан
    lcd.setCursor(0,1);
    lcd.print("   unverified   ");
    delay(2000);
    lcd.clear();
    lcd.print("Please try again");
    lcd.setCursor(0,1);
    lcd.print("Use your pointer"); //pointer - указательный палец (можете использовать любой и заменить это слово)
    delay(500);
  }
  delay(250);
}


void beep(){ 
  //издаёт звуки, чтобы кто-нибудь закрыл кейс
  lcd.clear();
  lcd.print("Please close the");
  lcd.setCursor(0,1);
  lcd.print("     case!      ");
  for(int i=0;i=80 && !fps.IsPressFinger()){
      beep();
    }
  } 
  timer = 0; //обнуляет таймер как только функция завершится
}

String centerText(String s) { //центрует текст на дисплее, чтобы он лучше смотрелся
  while(16-s.length()>1){ //если текст нуждается в центровке
    s = " " + s + " "; //равномерно добавляет пробелы с обеих сторон
  }
  return s;
}

Файлы

Шаг 9: Напечатанный на 3Д-принтере кейс






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

Файлы

Шаг 10: Подготовка гаража







Чтобы открывать гараж, я подключил ATtiny85 к кнопке, которая открывала гараж до этого. Вместо физического соединения, ATtiny использует в качестве «кнопки» NPN-транзистор.

Провода нужно измерить и отрезать по нужной длине, оставив немного длины про запас. Затем нужно припаять провода от кнопки к модулю сканера. Затем всё нужно хорошо изолировать.

Чтобы ATtiny внутри гаража получал сигналы от ATmega, находящегося снаружи гаража, я пропустил сквозь стену 3 провода (питание, землю и сигнальный). В гараже была деревянная стойка, через которую я и проделал отверстие.

Наконец, можно привинтить кейс и запустить устройство!

Шаг 11: Тестирование



Этот шаг будет самым весёлым. Используйте встроенную функцию добавления данных, чтобы ваши семья/друзья могли открывать гараж. Затем вы можете создать персональные сообщения для каждого из них! Посмотрите видео для визуального объяснения функционала девайса.

Шаг 12: Делаем устройство портативным




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

Заметка: я обнаружил, что 9V батарейки не хватало для одновременной работы девайса и сервопривода, поэтому заменил её на 6 батареек AA. Также хочу заметить, что дизайн моего замка был разработан в ознакомительных целях. Чтобы сделать его более безопасным, я бы рекомендовал использовать более жесткий корпус.