Raspberry Pi based Biometric Attendance System with Temperature Recorder and Sanitizer Dispenser

Published  February 24, 2022   0
J Joel Joseph
Author
Raspberry Pi based Biometric Attendance and Temperature Recorder System

What I have made here is an attendance register that records the temperature of a registered user and dispenses sanitizer. This was an idea that came up to me while I had to attend offline classes. Every single day a staff member would sit in front of the entrance and would take note of the student's name, their phone number, and temperature. So this got me thinking "why can't we just automate this?", I mean, that's the entire essence of automation right? to perform repeated tasks without any human intervention. Moreover, this project abides with the current social distancing policies enforced by our government. The machine identifies a registered user via face recognition, measures temperature, and then dispenses sanitizer. Another add-on feature is that the collected temperature data (along with the corresponding name and phone number) would be sent as an excel file via email to the intended receiver. This project can be used in offices or any educational institution as these are the places where attendance is mainly recorded.

Component Required for Attendance and Temperature Recorder

Project Used Hardware

  • HC-SR04 Ultrasonic Sensor Module (x3),
  • Arduino UNO R3 Development Microcontroller Board,
  • Raspberry Pi 4 8GB with 5MP Camera Board Module,
  • MLX90614 Contactless Ir Infrared Temperature Sensor Module,
  • MG995 TowerPro Servo Motor,
  • 20x4 Line LCD Display,
  • I2C SP Serial Interface Module Port For LCD display,
  • 5v Active buzzer

Project Used Software

  • Arduino IDE,
  • Geany,
  • face_recognition,
  • i2cLcd python module

Project Hardware Software Selection

I have included three HC-SR04 Ultrasonic sensors which act as triggers for the raspberry pi camera, the MLX90614 IR temperature sensor, and the M6995 servo motor. The MLX90614 IR temperature sensor reads the temperature of the user and the servo motor helps in dispensing sanitizer. A 20x4 LCD display has been used with an i2c serial interface module. The reason for including the i2c module was to save pin space as the LCD alone uses 16 pins whereas, with the i2c module, we just need 4. At its core, I have used both an Arduino UNO and a Raspberry Pi 4. The RPI4 has more computing power than the Arduino but the Arduino is more capable of handling sensor related functions. So that’s why I decided to take the best from both worlds and bridged them via serial ports. The 5v active buzzer is connected to the raspberry pi for high temperature alerts Arduino IDE was used for admin/uploading code to the arduino. For writing python code I made use of the Geany IDE. The face_recognition python module was used for face recognition and the i2cLcd python module was used to control the lcd.

Circuit Diagram

 

Raspberry Pi Temperature Recorder and Sanitizer Dispenser Circuit Diagram

Code
import face_recognition
import os
import pickle
import serial
import ast
import numpy as np
import drivers
import time
import RPi.GPIO as gpio
gpio.setmode(gpio.BOARD)
buzzerpin = 40
gpio.setup(buzzerpin,gpio.OUT)
display = drivers.Lcd()
devices = ['/dev/ttyACM0','/dev/ttyACM1']
for device in devices:
 try:
  ser = serial.Serial(device, 9600 , timeout=1)
  ser.flush()
  break;
 except Exception as e :
     continue;
def collect_send_data(send_string) :
    print(send_string)
    ser.write(send_string.encode('utf-8'))
def find_ip():
    ip=os.popen('hostname -I').read()
    ip = ip.replace("\n","")
    if ip=="" :
     ip = ' NO INTERNET' 
    return ip
face_image = []
face_encodings = {}
phone_numbers = {}
attendees = []
with open('/home/pi/faceR_prj/encodings.dat','rb') as f:
    face_encodings = pickle.load(f)
known_faces = np.array(list(face_encodings.values()))     
known_face_names = list(face_encodings.keys())
time.sleep(5)
while(1):
 display.lcd_clear()
 display.lcd_display_string("SYSTEM ACTIVE", 1)
 display.lcd_display_string("YOUR IP:"+find_ip(),3)
 collect_send_data(',')
 while(1):
    if find_ip()==' NO INTERNET' :
      find_ip()
    message_recieved = ser.readline().decode('utf-8')
    if '1' in message_recieved:
        print(message_recieved)
        break
    if 's' in message_recieved:
        print(message_recieved)
        continue   
 display.lcd_clear()
 display.lcd_display_string("     SAY CHEESE!!", 2)
 os.system('raspistill -w 640 -h 480 -t 2000 -o /home/pi/faceR_prj/pic.jpg')
 unknown_image = face_recognition.load_image_file("/home/pi/faceR_prj/pic.jpg")
 display.lcd_clear()
 os.system('sudo rm -r /home/pi/faceR_prj/pic.jpg')
 try:
    unknown_face_encoding = face_recognition.face_encodings(unknown_image)[0]
 except IndexError:
    print("NO FACE DETECTED")
    display.lcd_clear()
    display.lcd_display_string("NO FACE DETECTED", 1)
    display.lcd_display_string("PLEASE TRY AGAIN", 2)
    collect_send_data(',')
    time.sleep(3)
    display.lcd_clear()
    continue
 matches = face_recognition.compare_faces(known_faces,unknown_face_encoding,0.46)
 print(matches)
 name = "Unknown"
 face_distances = face_recognition.face_distance(known_faces,unknown_face_encoding)
 best_match_index = np.argmin(face_distances)
 if matches[best_match_index]:
                name = known_face_names[best_match_index]           
      
 if name == "Unknown":
   display.lcd_clear()
   display.lcd_display_string("   UNREGISTED USER", 1)
   display.lcd_display_string("   PLEASE CONTACT " ,2)
   display.lcd_display_string("  YOUR LOCAL ADMIN", 3)
   collect_send_data(',')
   time.sleep(3)
 elif name in attendees:
     display.lcd_clear()
     display.lcd_display_string("   USER DATA  ", 1)
     display.lcd_display_string("  WAS PREVIOUSLY  " ,2)
     display.lcd_display_string("    LOGGED  " ,3)
     collect_send_data(',')
     time.sleep(3)
 else:  
   attendees.append(name)  
   display.lcd_clear()
   display.lcd_display_string("WELCOME :", 1)
   display.lcd_display_string(name.upper(), 2)
   time.sleep(2)
   display.lcd_clear()
   display.lcd_display_string("BRING YOUR ARM TO ", 1)
   display.lcd_display_string("MEASURE TEMPERATURE", 2)
   time.sleep(2)
   collect_send_data('*')
   flag=0
   for num in range(0,6):
     message_recieved = ser.readline().decode('utf-8')
     if message_recieved:
       flag=1 
       display.lcd_clear()
       LEN = len(message_recieved)
       message_recieved = message_recieved.replace(message_recieved[LEN-1],"")
       message_recieved = message_recieved.replace(message_recieved[LEN-2],"")       
       temperature = ast.literal_eval(message_recieved)
       attendees.append(name)
       NewName = name
       NewName = NewName.replace(" ","_")
       os.system('python3 /home/pi/faceR_prj/attendence.py '+NewName+' '+message_recieved)
       display.lcd_display_string("TEMPERATURE : "+message_recieved, 1)
       time.sleep(2)
       if temperature < 36 :
            sanflag=0
            display.lcd_clear()
            display.lcd_display_string("PLEASE SANITIZE", 1)
            display.lcd_display_string("BEFORE YOU LEAVE", 2)
            time.sleep(2)
            collect_send_data('#')
            for num in range(0,6):
              message_recieved = ser.readline().decode('utf-8')
              if message_recieved :
                 sanflag = 1
                 display.lcd_clear()
                 display.lcd_display_string("   HAVE A NICE",2)
                 display.lcd_display_string("       DAY",3)
                 time.sleep(2)
                 break
              else:   
                 numstr =str(num)
                 display.lcd_display_string("["+numstr+"/5s TILL TIME OUT]", 3)
                 time.sleep(1)
            if sanflag == 0:
                display.lcd_clear()
                display.lcd_display_string(" TIME OUT ",2)
                time.sleep(2)      
       else:
         display.lcd_clear()
         display.lcd_display_string("****WARNING****",1)
         display.lcd_display_string("TEMPERATURE VALUE",2)
         display.lcd_display_string("  EXCEEDS NORMS",3)
         display.lcd_display_string("  ACCESS DENIED",4)
         gpio.output(buzzerpin,gpio.HIGH)  
         collect_send_data('a')   
         time.sleep(5)
         gpio.output(buzzerpin,gpio.LOW)
         time.sleep(1)
       break
     else :
       numstr =str(num) 
       display.lcd_display_string("["+numstr+"/5s TILL TIME OUT]", 3)
       time.sleep(1)   
   if flag == 0:   
        display.lcd_clear()
        display.lcd_display_string("   TIME OUT", 2)
        time.sleep(2)
        collect_send_data('a')
        time.sleep(2)
 print(name)

Mailer.py


import email, smtplib, ssl

from datetime import date ,datetime

import pickle

import os

from email import encoders

from email.mime.base import MIMEBase

from email.mime.multipart import MIMEMultipart

from email.mime.text import MIMEText

import openpyxl

from openpyxl import load_workbook

from openpyxl.styles import Font
wb = openpyxl.Workbook()
TriggerTime = "12:14:00"
def Trigger():
    current_time = str(datetime.now().strftime("%H:%M:%S"))
    #print(current_time)
    if TriggerTime == current_time:
        return 1
    return 0
while(1):
 if Trigger() == 0 :
     continue      
 subject = "Today's attendence"
 body = "Data collected from covisafe"
 sender_email = ""    
 receiver_email = ""
 password = ""
 message = MIMEMultipart()
 message["From"] = sender_email
 message["To"] = receiver_email
 message["Subject"] = subject
 message["Bcc"] = receiver_email
 message.attach(MIMEText(body, "plain")
 sheet = wb.active
 temperatures={}
 phone_numbers={}
 try:
     with open('/home/pi/faceR_prj/temperature.dat','rb') as f:
         temperatures=pickle.load(f)
     with open('/home/pi/faceR_prj/phonenos.dat','rb') as P:
         phone_numbers=pickle.load(P)
     names = list(temperatures.keys())        
     FONT = Font(bold=True)
     sheet.cell(row=1, column=1).value="Name"
     sheet.cell(row=1, column=2).value="Phone Number"
     sheet.cell(row=1, column=3).value="Temperature"
     sheet['A1'].font = FONT
     sheet['B1'].font = FONT
     sheet['C1'].font = FONT
     i=2
     for name in names:
        sheet.cell(row=i, column=1).value=name
        sheet.cell(row=i, column=2).value=phone_numbers[name]
        sheet.cell(row=i, column=3).value=temperatures[name]
        i=i+1
     sheet.column_dimensions['A'].width = 20
     sheet.column_dimensions['B'].width = 20
     sheet.column_dimensions['C'].width = 20
 except :
      FONT = Font(bold=True)
      sheet.cell(row=1, column=1).value="N/A"
      sheet.cell(row=1, column=2).value="N/A"
      sheet.cell(row=1, column=3).value="N/A"
      sheet['A1'].font = FONT
      sheet['B1'].font = FONT
      sheet['C1'].font = FONT
 Date = str(date.today())
 wb.save("/home/pi/faceR_prj/Register/"+Date+".xlsx")
 filename = Date+".xlsx" 
 with open("/home/pi/faceR_prj/Register/"+filename, "rb") as attachment:
    part = MIMEBase("application", "octet-stream")
    part.set_payload(attachment.read())
 encoders.encode_base64(part)
 part.add_header(
    "Content-Disposition",
    f"attachment; filename= {filename}",
 )
 message.attach(part)
 text = message.as_string()
 context = ssl.create_default_context()
 try:
     with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server:
         server.login(sender_email, password)
         server.sendmail(sender_email, receiver_email, text)
         os.system("echo "" > /home/pi/faceR_prj/temperature.dat")
 except:
     print("No internet access. Unable to send mail")

Attendance.py
import sys
import pickle
arg1 = sys.argv[1].replace("_"," ")
arg2 = sys.argv[2]
attendee = {}
try:
            with open('/home/pi/faceR_prj/temperature.dat','rb') as f:
                        attendee=pickle.load(f)
                        attendee[arg1]=arg2                       
except:
            attendee[arg1] = arg2         
print(attendee)         
with open('/home/pi/faceR_prj/temperature.dat','wb') as p:
    pickle.dump(attendee,p) 

Adduser.py
import pickle
import os
import face_recognition
face_encoding = {}
Name = input("Name: ")
Phoneno = input("Phone number :")
NewName = Name
NewName = NewName.replace(" ","_")
os.system('raspistill -w 640 -h 480 -o /home/pi/faceR_prj/oldfiles/'+NewName+'.jpg')
try:
 with open('/home/pi/faceR_prj/encodings.dat','rb') as F:
    face_encoding = pickle.load(F)
except EOFError:
    face_encoding = {}   

try:
 with open('/home/pi/faceR_prj/phonenos.dat','rb') as P:
    Phonenos = pickle.load(P)   
except EOFError:
    Phonenos = {}  
try:
    face_encoding[Name] = face_recognition.face_encodings(face_recognition.load_image_file('/home/pi/faceR_prj/oldfiles/'+NewName+'.jpg'))[0]
    Phonenos[Name] = Phoneno
except IndexError:

    print("NO FACE DETECTED...")

    quit()

with open('/home/pi/faceR_prj/encodings.dat','wb') as f:

    pickle.dump(face_encoding,f)

with open('/home/pi/faceR_prj/phonenos.dat','wb') as p:

    pickle.dump(Phonenos,p)   

os.system('sudo rm -r /home/pi/faceR_prj/oldfiles/'+NewName+'.jpg')

print("User added to database")

Arduino Code

#include <Wire.h>

#include <LiquidCrystal_I2C.h>

#include <Adafruit_MLX90614.h>

#include <Servo.h>

int trigpin1=12;

int echopin1=11;

int trigpin2=7;

int echopin2=6;

int trigpin3=8;

int echopin3=9;

int servopin = 3;

int pingtraveltime;

int pingvalue;

int Length;

Adafruit_MLX90614 mlx = Adafruit_MLX90614();

LiquidCrystal_I2C lcd(0x3F,20,4);

Servo servo ;

int ultrasonic(int trigpin,int echopin)

{

   digitalWrite(trigpin,LOW);

  delayMicroseconds(10);

  digitalWrite(trigpin,HIGH);

  delayMicroseconds(10);

  digitalWrite(trigpin,LOW);

  delayMicroseconds(10);

  pingtraveltime=pulseIn(echopin,HIGH);

  delay(25);

  delay(1000);

  return pingtraveltime;

}

void setup() {

  // put your setup code here, to run once:

 pinMode(trigpin1,OUTPUT);

 pinMode(echopin1,INPUT);

 pinMode(trigpin2,OUTPUT);

 pinMode(echopin2,INPUT);

 pinMode(trigpin3,OUTPUT);

 pinMode(echopin3,INPUT);

 Serial.begin(9600);

 mlx.begin(); 

 servo.attach(servopin);

 servo.write(0);

 lcd.init();

 lcd.backlight();

 lcd.init();

 lcd.setCursor(1,1);

 lcd.print("Booting");

}

void loop() {

  String incoming_data;

 while(Serial.available() == 0 )

     {}     

      incoming_data =  Serial.readStringUntil("\n");

  Length = incoming_data.length()   ;

 if(incoming_data[Length-1] == ',')

 {

  while(1)

  {

    pingvalue = ultrasonic(trigpin1,echopin1) ;

   if(pingvalue > 100 && pingvalue < 5000)

    {

     Serial.println('1');

     break;

    }

   else

    {

     Serial.println('s');

    }

  }

 }

  while(Serial.available() == 0 )

     {}     

      incoming_data =  Serial.readStringUntil("\n");

  Length = incoming_data.length();

  int count = 0;

  if(incoming_data[Length-1]=='*')

    {

   while(count<7)

   { 

       pingvalue = ultrasonic(trigpin2,echopin2);

        if(pingvalue > 100 && pingvalue < 2000)

         {

           double temperature_in_c = mlx.readObjectTempC();

           delay(500);

           String value = String(temperature_in_c);

           Serial.println(value);

           delay(2000);

           break; 

         }

         else

          delay(1000);

         count++;

    }

  count =0;

  while(Serial.available() == 0 )

     {}     

      incoming_data =  Serial.readStringUntil("\n");

  Length = incoming_data.length();

  if(incoming_data[Length-1]=='#')

  {

   while(count<7)

   { 

       pingvalue = ultrasonic(trigpin3,echopin3);

        if(pingvalue > 100 && pingvalue < 2000)

         {

           servo.write(0);

           delay(1000);

           servo.write(140);

           delay(1000);

           servo.write(0);

           delay(1000);

           Serial.println("STOP");

           break; 

         }

        else

          delay(1000);

         count++;

    } 

  }

 }

}


Video

Have any question realated to this Article?

Ask Our Community Members