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
Complete Project 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
#include
#include
#include
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++;
}
}
}
}