Zamek z czytnikiem linii papilarnych

Zamek z czytnikiem linii papilarnych

Pierwsze konstrukcje pełniące funkcje zamków – rygli, pochodzą z Asyrii, sprzed około sześciu tysięcy lat. Idea takiego wynalazku przetrwała do dzisiaj, jednak postęp technologiczny całkowicie ją przekształcił. Obecnie często używa się zamków elektronicznych, do których nie trzeba nawet klucza – wystarczy kod, odcisk palca czy zwykły uśmiech.

Zamki biometryczne, czyli takie, które potrafią odczytać pewne unikalne cechy naszego ciała i traktują je jako klucz, są znane już od dłuższego czasu. Najstarszymi i najpopularniejszymi z nich, są zamki, które wykorzystują odcisk naszego palca. Każdy człowiek ma inny układ linii papilarnych na dłoni, co czyni z nich klucz idealny. Współczesne zautomatyzowane czytniki linii papilarnych dostępne są, jako kompaktowe moduły, umożliwiające proste skanowanie i rozpoznawanie odcisków.

Autor tego projektu mieszka w małym mieszkaniu w Shenzhen z żoną i dzieckiem w wieku 5 miesięcy. „Czasami muszę wyjść, na przykład na dół do sklepu po cukier czy pieluchy dla mojego dziecka. Ale zawsze zapominam nosić ze sobą klucze, więc muszę zapukać do drzwi lub zadzwonić do żony, aby otworzyła mi drzwi. Przy tej okazji dziecko zawsze budzi się i zaczyna płakać” opisuje swoją motywację do budowy tego urządzenia autor. Jego pierwszym pomysłem był zakup gotowego zamka z czytnikiem linii papilarnych, ale takich zamków na rynku jest mnóstwo a w dodatku są one drogie i kłopotliwe w instalacji. Stąd też pomysł samodzielnego skonstruowania takiego zamka z zastosowaniem gotowych, dostępnych modułów dla Arduino.

Potrzebne elementy

Zanim przystąpimy do kompletowania elementów do konstrukcji zamka, należy przyjrzeć się naszym drzwiom i temu, jak są kontrolowane od strony mieszkania. Pozwoli to w prosty sposób zmechanizować nasz zamek, aby bez konieczności poważnych przeróbek można było sterować nim elektronicznie.

Fotografia 1. Zamek w drzwiach autora projektu. Na czerwono oznaczono zapadkę, która służy do otwierania

Na fotografii 1 został pokazany fragment drzwi i zamontowany na nich zamek w domu autora projektu. Jak zaznaczono na zdjęciu, dopóki zaznaczona na czerwono część pozostaje odciągnięta w kierunku strzałki, drzwi można otworzyć bez klucza. Jeśli element ten pozostaje zwolniony – zaznaczona część wraca do swojej pierwotnej lokalizacji i drzwi zostają zamknięte. Wystarczy, zatem odpowiednie umocowanie pojedynczego serwomechanizmu, aby kontrolować drzwi z poziomu Arduino.

Kluczowym elementem będzie skaner odcisków palców. To ten element pozwoli określić, czy dana osoba może wejść do domu. Ponadto niezbędny będzie moduł Arduino, który umożliwi komunikację z czytnikiem i będzie kontrolował serwomechanizm.

Fotografia 2. Elementy potrzebne do budowy zamka

Lista zastosowanych przez autora elementów prezentuje się następująco (fotografia 2):

  • Moduł Crowduino – płytka mikrokontrolera kompatybilna z Arduino. Jest tańsza niż oryginał, a poza tym ma pewne ulepszenia w projekcie, takie jak przycisk resetowania z boku. Można użyć zamiast niej dowolną inną płytkę z mikrokontrolerem, kompatybilną z Arduino;
  • Czujnik odcisków palców – autor wykorzystał moduł oznaczony SOT6439F (fotografia 3). Jest on wyposażony w interfejs UART, a dodatkowo dostępne są biblioteki do Arduino do jego obsługi;
  • Serwomotor z wystarczającym momentem obrotowym do przesunięcia zapadki lub odblokowania drzwi – zależnie od konstrukcji naszych drzwi, którymi chcemy sterować;
  • Klej lub cokolwiek innego, co pozwoli zamocować całą konstrukcję na drzwiach;
  • Zasilacz USB 5 V lub zasilacz 9 V ze złączem baryłkowym;
  • Przewody połączeniowe.
Fotografia 3. Zastosowany czytnik linii papilarnych SOT6439F

Schemat połączeń elektrycznych

W układzie znajdują się tylko dwa moduły, więc schemat połączeń jest bardzo prosty. Do modułu z mikrokontrolerem należy podłączyć dwa elementy – serwo oraz czytnik linii papilarnych, zgodnie ze schematem pokazanym na rysunku 1.

Rysunek 1. Schemat połączeń modułów w omawianym zamku

Serwomechanizm do działania potrzebuje podłączonego zasilania oraz pojedynczej linii danych, na której moduł Crowduino będzie wytwarzał sygnał o zmiennym wypełnieniu (PWM). Serwo podłączone jest w następujący sposób:

  • czerwony przewód z serwa – zasilanie 5 V (z zasilacza lub modułu z mikrokontrolerem),
  • brązowy przewód z serwa – masa systemu (z zasilacza lub modułu z mikrokontrolerem),
  • żółty przewód z serwa – linia D6 Crowduino.

Moduł czytnika linii papilarnych komunikuje się z Crowduino za pomocą interfejsu szeregowego, ale ponieważ Crowduino ma tylko jeden sprzętowy port szeregowy, którego potrzebuję do komunikacji z komputerem, autor zdecydował się na używanie Arduino Soft Serial do komunikacji ze skanerem odcisku palca. Moduł podłączony jest do Crowduino w następujący sposób:

  • czerwony przewód ze skanera – zasilanie 5 V (z zasilacza lub modułu z mikrokontrolerem),
  • brązowy przewód ze skanera – masa systemu (z zasilacza lub modułu z mikrokontrolerem),
  • purpurowy przewód ze skanera – linia A4 Crowduino,
  • żółty przewód ze skanera – linia A5 Crowduino.

Oprogramowanie

Firmware modułu zostało napisane z użyciem Arduino IDE. Szkic oprogramowania można pobrać ze strony opisującej projekt. Wystarczy otworzyć go w Arduino IDE, aby skomplikować i wgrać go do pamięci mikrokontrolera. Kod programu znajduje się na listingu 1. Działanie jest bardzo proste ponieważ korzysta z zewnętrznych bibliotek do obsługi programowego portu szeregowego i komunikacji z modułem czytnika linii papilarnych.

Listing 1. Kod programu kontrolującego zamek i skaner linii papilarnych

#include <Adafruit_Fingerprint.h>
#include <SoftwareSerial.h>
#include <Streaming.h>
#include <Servo.h>

// uruchamianie w trybie debugowym
#define __Debug         1

// pin serwomotora
const int pinServo      = 6;
// kąt obrotu serwomotora
const int angleServo    = 60;

#if __Debug
#define DBG(X)
#else
#define DBG(X)
#endif
Serial.println(X)

// Linie TX i RX interfejsu szeregowego
SoftwareSerial mySerial(A5, A4);

Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);
// Obiekt kontrolujący serwomotor
Servo myservo;

void open_close_door(){
   myservo.attach(pinServo);
   for(int i=20; i<angleServo; i++){
       myservo.write(i);
       delay(5);
   }
   delay(2000);
   for(int i=(angleServo-1); i>=20; i--){
       myservo.write(i);
       delay(5);
   }
   myservo.detach();
}

void setup(){
   Serial.begin(38400);
   finger.begin(19200);
   delay(500);
   DBG("setup ok!");
}

void loop(){                     
   if(getFingerprintIDez()>=0){
       open_close_door();
       DBG("get right finger, open door now!!");
       
       delay(2000);
   }
   delay(50);
}

// Zwraca ID odcisku palca lub -1, jeśli nie jest rozpoznany
int getFingerprintIDez(){
   if (!finger.verifyPassword()){
       DBG("Did not find fingerprint sensor :(");
       return -1;
   }
   uint8_t p = finger.getImage();
   if (p != FINGERPRINT_OK){
       return -1;
   }
   p = finger.image2Tz();
   if (p != FINGERPRINT_OK){
       return -1;
   }
   p = finger.fingerFastSearch();
   if (p != FINGERPRINT_OK){
       return -1;
   }
#if __Debug
   Serial.print("Found ID #");
   Serial.print(finger.fingerID);
   Serial.print(" with confidence of ");
   DBG(finger.confidence);
#endif
   return finger.fingerID;
}

W pętli głównej co 50 ms sprawdzane jest ID linii papilarnych, odczytanych przez skaner. Jeśli nie odczyta on żadnych linii papilarnych lub odczyta odcisk, który nie znajduje się na liście zapamiętanych w czytniku odcisków palców, zwraca wartość –1. Każda inna odpowiedź skanera spowoduje, że system poruszy serwomechanizmem o ustalony kąt i odczeka dwie sekundy, przed powrotem do regularnego sprawdzania skanera.

Zapisywanie odcisku palca

Zamek bazuje na odciskach palców zapisanych w pamięci czytnika. Zatem zanim zamek zacznie funkcjonować w drzwiach trzeba zapisać w nim wszystkie odciski palców, które otwierać mają drzwi. Aby to zrobić, należy zaprogramować mikrokontroler innym szkicem – został on pokazany na listingu 2. Dopiero wtedy można przystąpić do programowania czytnika.

Listing 2. Program umożliwiający zapisywanie odcisków w pamięci skanera

#include <Adafruit_Fingerprint.h>
#include <Streaming.h>
#include <SoftwareSerial.h>

uint8_t getFingerprintEnroll(uint8_t id);

// Linie TX i RX interfejsu szeregowego
SoftwareSerial mySerial(A5, A4);                    

Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);

void setup()
{
   Serial.begin(38400);
   finger.begin(19200);
   if (finger.verifyPassword()){
       Serial.println("Fingerprint sensor init ok");
   }
   else {
       Serial.println("Did not find fingerprint sensor :(");
       while (1);
   }
}

void loop(){
   Serial.println("Type in the ID # you want to
   save this finger as...\r\n(end with an ‘a’, such as 15a)");
   uint8_t id = 0;
   while (Serial.available()){
       char c = Serial.read();
   }
   while (true){
       while (! Serial.available());
       char c = Serial.read();
       if (! isdigit(c)){
           while(Serial.available()){
               c = Serial.read();
           }
           break;
       }
       id *= 10;
       id += c - ‘0’;
   }  
   Serial.print("Enrolling ID #");
   Serial.println(id);
   while (!getFingerprintEnroll(id));
}

uint8_t getFingerprintEnroll(uint8_t id){
   uint8_t p = -1;
   Serial.println("Waiting for valid finger to enroll");
   while (p != FINGERPRINT_OK) {
       p = finger.getImage();
       switch (p) {
           case FINGERPRINT_OK:
           Serial.println("Image taken");
           break;
           case FINGERPRINT_NOFINGER:
           break;
           case FINGERPRINT_PACKETRECIEVEERR:
           Serial.println("Communication error");
           break;
           case FINGERPRINT_IMAGEFAIL:
           Serial.println("Imaging error");
           break;
           default:
           Serial.println("Unknown error");
           break;
       }
   }
   p = finger.image2Tz(1);
   switch (p) {
       case FINGERPRINT_OK:
       Serial.println("Image converted");
       break;
       case FINGERPRINT_IMAGEMESS:
       Serial.println("Image too messy");
       return p;
       case FINGERPRINT_PACKETRECIEVEERR:
       Serial.println("Communication error");
       return p;
       case FINGERPRINT_FEATUREFAIL:
       Serial.println("Could not find fingerprint features");
       return p;
       case FINGERPRINT_INVALIDIMAGE:
       Serial.println("Could not find fingerprint features");
       return p;
       default:
       Serial.println("Unknown error");
       return p;
   }
   Serial.println("Remove finger");
   delay(2000);
   p = 0;
   while (p != FINGERPRINT_NOFINGER) {
       p = finger.getImage();
   }
   p = -1;
   Serial.println("Place same finger again");
   while (p != FINGERPRINT_OK) {
       p = finger.getImage();
       switch (p) {
           case FINGERPRINT_OK:
           Serial.println("Image taken");
           break;
           case FINGERPRINT_NOFINGER:
           break;
           case FINGERPRINT_PACKETRECIEVEERR:
           Serial.println("Communication error");
           break;
           case FINGERPRINT_IMAGEFAIL:
           Serial.println("Imaging error");
           break;
           default:
           Serial.println("Unknown error");
           break;
       }
   }
   p = finger.image2Tz(2);
   switch (p) {
       case FINGERPRINT_OK:
       Serial.println("Image converted");
       break;
       case FINGERPRINT_IMAGEMESS:
       Serial.println("Image too messy");
       return p;
       case FINGERPRINT_PACKETRECIEVEERR:
       Serial.println("Communication error");
       return p;
       case FINGERPRINT_FEATUREFAIL:
       Serial.println("Could not find fingerprint features");
       return p;
       case FINGERPRINT_INVALIDIMAGE:
       Serial.println("Could not find fingerprint features");
       return p;
       default:
       Serial.println("Unknown error");
       return p;
   }
   p = finger.createModel();
   if (p == FINGERPRINT_OK) {
       Serial.println("Prints matched!");
   } else if (p == FINGERPRINT_PACKETRECIEVEERR) {
       Serial.println("Communication error");
       return p;
   } else if (p == FINGERPRINT_ENROLLMISMATCH) {
       Serial.println("Fingerprints did not match");
       return p;
   } else {
       Serial.println("Unknown error");
       return p;
   }
   p = finger.storeModel(id);
   if (p == FINGERPRINT_OK) {
       Serial.println("Stored!");
   } else if (p == FINGERPRINT_PACKETRECIEVEERR) {
       Serial.println("Communication error");
       return p;
   } else if (p == FINGERPRINT_BADLOCATION) {
       Serial.println("Could not store in that location");
       return p;
   } else if (p == FINGERPRINT_FLASHERR) {
       Serial.println("Error writing to flash");
       return p;
   } else {
       Serial.println("Unknown error");
       return p;
   }
}

Aby zapisać w pamięci czytnika odcisk należy otworzyć monitor szeregowy (można wykorzystać ten zawarty w Arduino IDE lub skorzystać z dowolnego innego programu terminalowego). Należy ustawić szybkość transmisji na 38400 i obserwować terminal. Po uruchomieniu programu zacznie wysyłać on monity o zeskanowanie i wprowadzenie odcisku palca. Skaner może przechowywać do 120 wzorów odcisków. Aby wprowadzić do pamięci nowy wzór należy wpisać numer ID, pod jakim zostanie zapisany odcisk i dodać do niego literę „a”, i następnie nacisnąć enter. Na przykład „1a” powoduje zapisanie odcisku pod ID 1. Następnie terminal wyświetli monit o wprowadzenie odcisku. Wystarczy położyć palec na skanerze i nacisnąć go, aby zeskanował on nasz palec. Po naciśnięciu wyjście na monitorze szeregowym przypomina o zdjęciu palca, a następnie prosi o ponowne naciśnięcie palcem skanera w celu podwójnego sprawdzenia poprawności zapisu. Wykorzystując wyjścia szeregowe, można łatwo wykonać wszystkie kroki.

Po zeskanowaniu wszystkich odcisków palców, jakie mają być używane, wystarczy wgrać ponownie szkic z listingu 1, aby system działał, jako normalny zamek.

Gotowy zamek

Wszystkie zapisane odciski palców skaner przechowuje w swojej nieulotnej pamięci EEPROM. Wystarczy nacisnąć skaner palcem i jeśli Crowduino rozpozna Twój palec, serwo obróci się o kilka stopni, aby otworzyć drzwi, a po 2 sekundach powróci do pierwotnego położenia. Serwo należy zamocować na zamku tak, aby naciskało rygiel.

Autor użył do tego zwykłej żywicy dwuskładnikowej, ale klej to nie jedyne rozwiązanie. System można dostosować do własnego zamka, korzystając np. z elementów z druku 3D itp. Na fotografii 4 pokazano gotowy serwomotor, zamocowany na zamku.

Fotografia 4. Sposób zamontowania serwomechanizmu do zamka

System może być zasilany za pomocą zasilacza 5 V, podłączonego do portu USB płytki z mikrokontrolerem lub zasilacza 9 V, dołączonego do odpowiedniego portu układu (wtedy wykorzystywany jest stabilizator 5 V na płytce Arduino). Jeśli w projekcie zastosowano inną płytkę z mikrokontrolerem, to zasilanie należy dostosować do wymagań tego modułu.

Nikodem Czechowski, EP

Źródło: https://bit.ly/3x9uqUb

Artykuł ukazał się w
Elektronika Praktyczna
sierpień 2021

Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik wrzesień 2021

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio wrzesień - październik 2021

Świat Radio

Magazyn krótkofalowców i amatorów CB

Automatyka Podzespoły Aplikacje wrzesień 2021

Automatyka Podzespoły Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna wrzesień 2021

Elektronika Praktyczna

Międzynarodowy magazyn elektroników konstruktorów

Elektronika dla Wszystkich wrzesień 2021

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów