Attendance Record System

I have been given a task to build an attendance system for staff. The requirements are quite straight-forward. It needs to be standalone (without computer), fingerprint is preferred, record real time and the record should be in SD card. 

1.0 Introduction

There has been various kinds of attendance record system in the market, RFID-based, fingerprint scanning-based and barcode-based, etc. Talking about this attendance system, you may think that it is quite complex and hard to build it. However, after you read this article, you may change your perception. In this tutorial, we are going to demonstrate how to make a simple attendance system using Cytron current on-sale product including Arduino board, SM630 fingerprint module, Arduino Ethernet Shield and other devices such as LCD display shield and RTC (Real Time Clock).

DSCF5587

1.1 Feature

The objective of this tutorial is to build an example of  standalone attendance record system.

Below are the features of the system:

  • built using Arduino Mega 2560 main board 
  • utilizes SM630 fingerprint module for fingerprint scanning purpose and extra storage. 
  • utilizes embedded microSD slot on Ethernet Shield to store attendance information which can be easily accessed by user.
  • includes LCD Keypad shield that displays time and attendance information.It also contains 5 buttons (not including reset button) in which the user can use them as navigation buttons.
  • includes RTC module to store real date and clock time information.
  • includes other device such as buzzer as indicator.

2.0 Getting Started

2.1 Components

The components you need:

Electronic components:

Indicator:

Fingerprint module UART connection:

Power System:

RTC Module System:

  • 1 x Arduino Type Header pin

2.2 Basic Connection:

The system is built by stacking up the main board and shields as shown below.

overall

Before stacking up, we need build a circuit consists of all necessary systems on prototyping shield.

prototype circuit

IMG_20130829_142106

2.21 Power System

Primary objective of this power system is to provides 5VDC to be used by Arduino board and other devices.

For your information, we have been experimenting the built-in power system but it turned out that current output is not enough for the current project. The use of LCD keypad shield and Ethernet shield consumes significant amount of current. The external power system is required to provide additional current.

User can use the sample power system circuit below. This power system can provide up to 8 Amps, and it shouldn’t be a problem to this project.The user can also make their own power system if it is appropriate.

Fullscreen capture 8262013 54715 PM

Note**: JP2 ,F1 and S1 can be ignored.

power system

In the circuit above, we only connect the 5V from Arduino board to the power system output. User can connect DC plug voltage output to on board Vin for other uses. Ethernet shield has an embedded 5V to 3V3 DC power system so for those users who want to use 3V3 applications don’t need to build another one.

2.22 Indicator

In this project, we add a buzzer in the system to indicate the completion of attendance record process. For example, one beep sound indicates login success and 2 beep sounds means logout. If there are 3 beep sounds, error occurs such as improper installation of microSD card, unfound employee fingerprint, etc.

In the circuit, user just needs to connect one leg (longer leg) of buzzer to 5V and another to GND.

User can customize their own indicator systems. User can even include some visual indicatiors like LEDs.

2.23 Fingerprint Module UART Connection

The figure below shows the connection between the connector and Arduino prototyping shield.

uart1

2.24 RTC module 

User can either directly solder the module to prototyping shield or solder a 5 pin or 6 pin header on the shield for easy removal of the module like we did.  This RTC module stores current time information with the power of a coin cell lithium battery.

Note**: Removal of the battery will instantly erase the information stored and reset the time. Handle with care!

We are going to use 4 pins only :VCC, SCL, SDA and GND. Leave others as not connected.

rtc2

You can either use jumper wires to connect to the legs of female header pin, or solder 2 wires onto the board as SDA and SCL wires.

rtc1

Connect the SDA wire to pin 20 and SCL to pin21. There should labels for you as guidance.

IMG_20130829_142511

While inserting battery, make sure the side of battery with “+” sign attached to the surface of coin cell holder with “+” sign.

2.3 Hardware setup

For your information, Ethernet shield and LCD keypad shield share same pins at pin 4 and pin 10. We are going to do some modification to prevent pin crashes.

Modification 1: Bend the leg of prototyping shield at pin 4 and 10.

hacking1

It should be something like photo below if you stack prototyping  shield onto Ethernet shield.

hacking

Modification 2: Connect pin 4 on prototyping shield to pin A3. We are going to use pin A3 as D4 for LCD.

OKAY, now we can proceed to programming stage!

3.0 Programming

Below is the flow diagram sketch of the designed system. The full sketch can be downloaded here.

flowchart

Basically the programming involves 4 major parts:

  • fingerprint scanning process and teaching process
  • information storing and accessing with fingerprint module and micro SD,
  • time settings and display
  • menu interface for time setup, fingerprint teaching ,etc.

i) FINGERPRINT SCANNING AND TEACHING

How does SM630 Fingerprint module work? It is using packet data communication. If we wants the module to carry out some operations, we need to send a string of bytes in serial to the device as command. After the device receives the data bytes, it will send back a string of bytes in serial as response. It is our job to do some programming to ‘write’ and  ‘read’ these bytes to determine the outcome of the device.

Generally the communication packet includes the following:

  • packet head – 2 bytes
  • packet flag – 1 byte
  • packet length – 1 byte
  • packet content – N bytes
  • checksum – 1 byte

Let say we want to add fingerprint at position(id) 2, what should we send?

  • packet head : 0x4D and 0x58
  • packet flag: 0x01 (command)
  • packet length:adding operation requires 3 data bytes for packet content, thus here we should put 0x03
  • packet content:  0x40 (command code as stated in user manual), 0x00(high byte of value 2), 0x02 (low byte of value 2)
  • checksum : 0xFA (low byte of total sum from packet head to packet content data)

So we should send a string of data bytes  {0x4D,0x58,0x01,0x03,0x40,0x00,0x02,0xFA} in serial to the fingerprint module.

This is just a brief explanation on the working principle of fingerprint module. To know more details on how to communicate with the module and execute other functions, user can refer to the SM630 Fingerprint Module User’s Manual.

Below is the sample code of sending command and receive response. This sample code is not optimized to full functionality. User can play around to add in more functions or modify the program for the best performance.

//define fingerprint command code
 #define ADD_FINGERPRINT 0x40
 #define DELETE_FINGERPRINT 0x42
 #define SEARCH_FINGERPRINT 0x44
 #define EMPTY_DATABASE 0x46
 #define MATCHING 0x4B

//define fingerprint response code
 #define OP_SUCCESS 0x31
 #define TIME_OUT 0x33
 #define PROCESS_FAILED 0x34
 #define PARAMETER_ERR 0x35
 #define MATCH 0x37
 #define FP_FOUND 0x39
 #define FP_UNFOUND 0x3A

Sending command

 Program Program description
void _cmd(byte cmd,int number)
{
 if(number>767)
 {
  msg("INVALID ID");
  return;
 }

 Serial.begin(57600);

 byte packet_content;
 switch(cmd)
 {
  case SEARCH_FINGERPRINT:packet_content=5;break;
  case EMPTY_DATABASE:packet_content = 1;break;
  default: packet_content = 3;break;
 }

byte Hbyte = number/256;
 byte Lbyte = number%256;
 byte checksum = 0;

byte send_cmd[packet_content+5];
for(byte i=0;i<sizeof(send_cmd);i++) 
 send_cmd[i]=0;

send_cmd[0] = 0x4D;
 send_cmd[1] = 0x58;
 send_cmd[2] = 0x10;
 send_cmd[3] = packet_content;
 send_cmd[4] = cmd;

for(byte i=0;i<sizeof(send_cmd);i++)
 checksum+=send_cmd[i];
 if(packet_content>=3)
 {
  send_cmd[5] = Hbyte;send_cmd[6] = Lbyte;
  checksum+=send_cmd[5];
  checksum+=send_cmd[6];
  if(cmd==SEARCH_FINGERPRINT)
  {
   for(byte i=7;i>4;i--)
   send_cmd[i+2]=send_cmd[i];
   send_cmd[5] = 0;send_cmd[6] = 0;
   checksum+=send_cmd[5];
   checksum+=send_cmd[6];
  }
 }

send_cmd[packet_content+5-1]=checksum;

Serial.write(send_cmd,sizeof(send_cmd));
 delay(1);
}
  • byte cmd refers to which command the user wants to execute.For example: if user wants to add fingerprint, cmd should be 0x40. int number refers to fingerprint id(position) in the module. If the command doesn’t require id number, just put any number.
  • The command should not be sent if id/position is more than 767 (the module can only store up to  the 768 fingerprints only), or else you will receive parameter error from device.
  • Initialize the serial communication at baud rate 57600 as stated in user manual.
  • packet_content will be determined by the cmd value you just inserted. Most of the command has packet content length of 3 bytes. Search fingerprint and empty database are the exceptions.
  • Determine high byte and low byte of the id number.
  • Declare an array of bytes with variable size to be sent to module and initialize it.
  • Start putting appropriate values to the array. 0x4D as first byte, 0x58 as second byte and so forth. Please refer to fingerprint module user manual to verify the correctness of packet data.
  • Calculate the checksum for first time.
  • If the packet content length equal or more than 3 bytes, add high byte of number as 5th byte and low byte as 6th byte. Calculate the checksum. Take note that commands with packet content length less than 3 bytes will not go through this part.
  • If the command is search fingerprint command, shift value from 5th byte to 7th byte and 6th byte to 8th byte. Now 5th byte and 6th byte should be replaced with high byte and low byte of a number respectively where this number is the number that module will start searching from. Here we put 0 in both bytes, thus searching will start from 0. Recalculate the checksum.
  • Put the value of final checksum as last byte.
  • Send this string of bytes to the module. Give some delays.

Receiving response

Program Program description
boolean _respond(byte cmd)
{
  boolean end_flag = false;
 boolean success = false;
 byte addfp_count = 0;
  while(!end_flag)
 {
   int packet_length = 9;
   byte resp_temp[packet_length];
    for(byte j=0;j<packet_length;j++)
   { 
     while(Serial.available()==0)
      {
        //you can add something here
      }
     resp_temp[j]=Serial.read();
     if(j==3) packet_length = resp_temp[3]+5;
    }

   switch(resp_temp[5])
   {
case OP_SUCCESS:
if(cmd==SEARCH_FINGERPRINT) 
//add something here
else if(cmd==ADD_FINGERPRINT) 
{ 
//add something here
     addfp_count++;
}
else if(cmd==DELETE_FINGERPRINT)
{
//add something here
end_flag = true;
success = true;
}
else if(cmd==EMPTY_DATABASE)
{
//add something here
     end_flag = true;
success = true;
}
    break;
    case TIME_OUT:
//add something here
    end_flag=true;
break;

    case PROCESS_FAILED:
//add something here
end_flag=true;
break;

    case PARAMETER_ERR:
//add something here
end_flag=true;
break;

    case MATCH:
    //add something here
success = true;
end_flag=true;
break;

    case NO_MATCH:
// add something here
end_flag=true;
break;
    
case FP_FOUND:
//add something here
    worker_id = response[6]*256+response[7];
end_flag=true;
success = true;
break;

    case FP_UNFOUND:
//add something here
end_flag=true;
break;
   }
  
if(cmd==byte(ADD_FINGERPRINT)&&addfp_count>1)
{
//add something here
    success = true;
end_flag=true;
}

}
 Serial.end();
 return success;
}
  • byte cmd refers to the previous command the user sends to module.
  • Initialize some variables
  • end_flag – determines when this function should terminate
  • success – determines whether the operation performs successfully
  • addfp_count – this variable is made for adding fingerprint process. Adding fingerprint process requires 2 responses to be successful. addfp_count determines when the function ends.
  • Create a temporary array for received packets. Initialise the array with the size of 9.
  • Read and store the received bytes. When 4th byte is read, total data packet length is determined.
  • Determine high byte and low byte of the id number.
  • Declare an array of bytes with variable size to be sent to module and initialize it.
  • In attendance system, search fingerprint command is constantly sent to module. If there is no finger on screen within about 10 secs, time out response will be received. Before this response is received, the program will continue in this loop(red highlighted). And also once the command is sent, fingerprint module will not receive any other commands and do anything until the response for the current command is given out. Therefore user can put in some functions like time display.
  • Check the 6th byte of received data and carry out functions according to response code
  • //add something here is the place where you can put your own functions like light up LED, make beep sound from buzzer or display words on LCD
  • this function is boolean/bool type, which returns value of success. User may modify the function to any appropriate types and return something in which user think is useful for next operation. User may change to void type to return nothing.
  • If search fingerprint is successful, 6th and 7th bytes will be the id number that module returns for corresponding fingerprint. User may want to define a variable to store the number. In this tutorial, we declare a global variable worker_id to store the number.

Fingerprint Searching

These 2 functions allows user to carry out fingerprint searching and teaching process. If user wants to start searching process, user can write the following lines in the program.

_cmd (SEARCH_FINGERPRINT,100);
_respond(SEARCH_FINGERPRINT);

These lines send a command to the module to search for matching fingerprint designated from id 0 to 100 and gives appropriate response.

Fingerprint Teaching

For fingerprint teaching process like adding fingerprint, user may write the following codes

int id = 10;
_cmd (ADD_FINGERPRINT,id);
_respond(ADD_FINGERPRINT);

The codes above allows user to add fingerprint with id 10 if the response indicates the process is successful. You can check the response from _respond function. You can also write a program to select a value for int id so you can add fingerprint with any id number less than 768. You can utilise the navigation buttons on LCD keypad shield to do so.

The code for deleting fingerprint is almost similar to adding fingerprint:

int id = 10;
_cmd (DELETE_FINGERPRINT,id);
_respond(DELETE_FINGERPRINT);

The codes above allows user to delete fingerprint with id 10 if the process is successful.

ii) INFORMATION STORING AND ACCESSING

Write and read info using SM630 fingerprint module

SM630 fingerprint module has 64K bytes user flash memory to store necessary information. It can serve as extra storage if user knows how to utilise this function. Below are the sample codes on how to write strings data into fingerprint flash memory and read data from it.

Write/store strings to flash

 Program Program description
void wrFlash(int id,int type,String data2wr)
{
 Serial.begin(57600);
 char data[data2wr.length()+1];
 data2wr.toCharArray(data,data2wr.length()+1);
 if(sizeof(data)>128) {msg("too big");return;}
 byte wr_data[sizeof(data)+9];
 byte checksum = 0;
 byte i;
 wr_data[0] = 0x4D;
 wr_data[1] = 0x58;
 wr_data[2] = 0x10;
 wr_data[3] = 4 + sizeof(data);
 wr_data[4] = 0x64;
 wr_data[5] = ((id-1)*ADDR + type)/256;
 wr_data[6] = ((id-1)*ADDR + type)%256;
 wr_data[7] = sizeof(data);
 for(i = 0;i<sizeof(data);i++) wr_data[i+8] = data[i];
 for(i=0;i<(sizeof(wr_data)-1);i++) checksum+= wr_data[i];
 wr_data[sizeof(wr_data)-1] = checksum;
 Serial.write(wr_data,sizeof(wr_data));
 delay(1);
 char rx[6];
 while(Serial.available()==0){}
 Serial.readBytes(rx,6);
 if(rx[3]==0x01&&rx[4]==0x02) 
{
//do something here if receive packet error
return;
}
 char ops_[7];
 while(Serial.available()==0){}
 Serial.readBytes(ops_,7);
 if(ops_[5]!=OP_SUCCESS) 
{
//do something here if storing data not successful
return;
}
 Serial.end();
}
  • This function is customized and int id and type are used to determine the address for the data to be stored. User can modify these 2 parameters.
  • Initialise the serial communication and convert string to char array.
  • According to user manual, only 128 bytes are allowed to be written at one time.
  • Initialise an array to store the bytes to be sent to the module and put necessary values to each byte.
  • wr_data[5] refers to the high byte of address and wr_data[6] refers to low byte of address. User may modify these 2 lines.
  • send the data bytes to module and waiting for response.
  • First response is to check whether it is packet error or not.
  • Second response is to check whether writing data to memory is successful or not.
  • Take note**: 1 address only stores 1 byte of data. Let say you want to store “Cytron” string, 7 consecutive addresses are needed (including null character). In this function you only have to send the starting address to the module. If you want to store multiple data, consider to plan the address properly or else you will find the data overlapped.

 Read data from flash

 Program Program description
char* rdFlash(int id,int type,byte num)
{ 
 Serial.begin(57600);
 if(num>128) {msg("too big");return 0;}
 byte rd_data[9];
 byte checksum = 0;
 byte i;
 rd_data[0] = 0x4D;
 rd_data[1] = 0x58;
 rd_data[2] = 0x10;
 rd_data[3] = 0x04;
 rd_data[4] = 0x62;
 rd_data[5] = ((id-1)*ADDR + type)/256;
 rd_data[6] = ((id-1)*ADDR + type)%256;
 rd_data[7] = num;
for(i=0;i<8;i++) checksum+= rd_data[i];
 rd_data[8] = checksum;
Serial.write(rd_data,sizeof(rd_data));
 delay(1);
 char rx[6];
 while(Serial.available()==0){}
 Serial.readBytes(rx,6);

if(rx[3]==0x01&&rx[4]==0x02) 
 {
  //do something here if receive packet error 
  return 0;
 }
 char ops_[6+num];
 while(Serial.available()==0){}
 Serial.readBytes(ops_,sizeof(ops_));

char dat[num];
 for(i=0;i<num;i++) dat[i] = ops_[i+5]; 
 Serial.end();
 return dat; 
}
  • This function is customized and int id and type are used to determine the starting address for the data to be read. User can modify these 2 parameters. byte num refers to the length of data you want to read.
  • Initialise the serial communication. Same as before, only 128 bytes are allowed to be read at one time.
  • Initialise an array to store the bytes to be sent to the module and put necessary values to each byte to initiate reading data from module.
  • rd_data[5] refers to the high byte of address and rd_data[6] refers to low byte of address. User may modify these 2 lines.
  • send the data bytes to module and waiting for response.
  • First response is to check whether it is packet error or not.
  • Second response from module contains the data bytes the user requests from address. User can create a variable to store these data bytes. Here we declare function as char array type and will return the data if the function ends. User can modify the function type to suit their application.
  • Note**: the only thing you have to care about is the num value. Make sure that you know the size of the data you want to read. Let say you want to read “Ahmad” from module you can set num = 8, 6 for the data(including null terminator) and 2 for extra length.

Write and read info using micro SD

There are a few things you have to do before you proceed.

First you have to make sure pin 4  and pin 10 from Ethernet Shield is not connected with the same pins  of LCD Keypad Shield. If you haven’t modified and disconnected these pins, you may refer to the instructions shown in 2.3 Hardware Setup.

Second, please include the latest SD library in your Arduino library directory. The file can be found and downloaded at the end of this tutorial. In this tutorial we are using this library.

Below are the sample codes for accessing,writing and reading data from micro SD.

#include <SdFat.h>
#define chipSelect 4  //CS pin for microSD
Sd2Card card;
SdVolume vol;
SdFat sd;

Checking micro SD condition

 Program Program description
boolean checkSDCard()
{ 
 boolean SDCard_status = card.init(SPI_HALF_SPEED,chipSelect);
if (!SDCard_status||!vol.init(&card)) 
 { 
  //do something here if card is not detected
 }
 uint32_t volumesize= vol.blocksPerCluster()*vol.clusterCount()*512;
 uint32_t freespace = 512*vol.freeClusterCount()*vol.blocksPerCluster();
 //check free space available
 if(freespace==0) 
 //do something here if memory is full
 else if(freespace<0.1*volumesize) 
 //do something here if memory is nearly full

 return true;
}
  • This function is to check SD card condition and returns true value if card is detected.
  • card.init is to initialise the card and vol.init is to initialise the accessing volume of the card. Both functions returns true if the process is completed. It is advisable to check these 2 functions to see whether the card is detected or not.
  • In this function, calculation of total volume size and free space available is included. User can use this to check the availability of the card to store data.

Write/Store data to microSD

This is the example on how we store employee’s login and logout information in our company. User may modify it to suit their application.

 Program Program description
void record(String dataString)
{ 
 // do something here to indicate recording/storing
 char filename[fileName.length()+1];
 fileName.toCharArray(filename,fileName.length()+1);
 //checking card condition
 if (!sd.begin(chipSelect, SPI_HALF_SPEED))
 {
  if(!checkSDCard())
  {
// do something if card failed
  }
 }
 boolean title=false;
 if(!sd.exists(filename)) title = true;
 // create or open file
 SdFile dataFile;
 if(title==true)
 { 
  SdFile::dateTimeCallback(dateTime);
  dataFile.open(filename,O_WRITE| O_CREAT| O_APPEND);
  dataFile.println("Date,Time,Worker ID,Status,Remark,");
  dataFile.close();
  title = false;
 }
 else dataFile.open(filename, O_WRITE| O_APPEND);

// if the file isn't open, pop up an error:
 if (!dataFile.isOpen()) 
 {
  // do something if file cannot be opened
 } 
 else
 {// if the file is available, write to it:
  dataFile.println(dataString);
  dataFile.close();
 }
  • This function is to store strings to a file in microSD. String dataString is the employee’s login and logout string info ready to be stored.
  • Highlighted lines are the codes to convert string to const char* type. This string is the target filename.
  • If the file doesn’t exist, create a new one. Function dateTimeCallback(dateTime) is to detemine the time when the file is created, so that when user is accessing the file using computer or other devices, he or she can see the proper created time and date for the file. If this function is not called, the date will be incorrect. This function is only working with the installation of RTC_module.
  • O_WRITE only in file open will cause the data in existing file to be overwritten. To store data without overwritting the existing data, use O_WRITE|O_APPEND instead.

Read file from microSD

 Program Program description
void rdAdmin()
{
 SdFile dataFile;
if (!sd.begin(chipSelect, SPI_HALF_SPEED))
 {
 if(!checkSDCard())
 {
  //do something here if card failed
 }
 }
if (!dataFile.open("admin.csv", O_READ))
 {
  //do something here if file not opened
 }
char data;
 String worker_data[5];
 int count = 0;
 boolean title_flag = true;
 int line_count = 0;

 while ((data = dataFile.read()) >= 0)
 {
 if(data!=','&&int(data)!=13&&int(data)!=10) 
  worker_data[count]+=String(data);
 else if(data==',') count++;
 else if(int(data)==13) //if read data jumps to next line
 {
 dataFile.read();
 if(line_count>0)
 {
  wrFlash((worker_data[0].toInt()),ID,worker_data[0]);
  wrFlash((worker_data[0].toInt()),NAME,worker_data[1]);
  wrFlash((worker_data[0].toInt()),POS,worker_data[2]);
 }
 line_count++;
 count = 0;
 memset(worker_data,0,sizeof(worker_data));
 }
 }
 dataFile.close();
 }
  • This function is to read employees’ info from a file named “admin.csv” in microSD.
  • dataFile.read() reads character by character. If no data can’t be read(at the end of file), data will be < 0.
  • This function also implements wrFlash function. The data read will be stored in fingerprint module flash memory.
  • An temporary array with size 5 is declared to store worker’s data, one for one type (although only 3 types to be stored).
  • The file read is in CSV format (comma separated), thus this algorithm uses comma to separate read data and store into different array.
  • If next line is reached, the arrays are reset so that new data can be stored. next line will be indicated by int data 13(carriage return)and 10(new line feed). User can refer to ASCII code table.
  • There is also a SD library function call fgets() which reads line by line. In this project some of the functions implement this. User can have a guide.

iii) TIME SETTINGS AND DISPLAY

Include the libraries below in your program. DS3231 RTC module is almost similar to DS1307 RTC and both chip are I2C devices and have the same working principle. Thus DS1307 library can be used for this project. Wire.h is for I2C configuration, LiquidCrystal.h is for LCD display and LCD_Key.h is for LCD Keypad button.

#include <Time.h>
#include <DS1307RTC.h>
#include <Wire.h>
#include <LiquidCrystal.h>
#include <LCD_Key.h>
//define keycode
#define None 0
#define Select 1
#define Left 2
#define Up 3
#define Down 4
#define Right 5
LiquidCrystal lcd(8, 9, A3, 5, 6, 7);
LCD_Key keypad;

Setup - Sync the system time with RTC
void setup()
{
 .....................// 
 lcd.begin(16,2);
 Wire.begin();
 setSyncProvider(RTC.get);
//....................
}
Remember to include setSyncProvider(RTC.get) in void setup() to synchronize the system time with RTC time. In this project, we are actually using the system time from Arduino to do time-critical application. Time.h library will handle this job for us and give us specific details about the time like year,month,day,week day, hour, minute and second. Without RTC sync, the system time will reset after the system is off even though we have set it to the right time.

Current Time Display
 Program Program description
void Time_Display()
{
lcd.setCursor(0,1);
 lcd.print(Wday[weekday()-1]);lcd.print(" ");
lcd.setCursor(0,0);
 printDigits(day());
 lcd.print("-");
 printDigits(month());
 lcd.print("-");
 printDigits(year()%100);
 lcd.setCursor(11,0);
printDigits(hourFormat12());
 lcd.print(":");
 printDigits(minute());
 //lcd.print(":");lcd.print(second());
 lcd.setCursor(14,1);
 if(isAM()==1){
 lcd.print("AM");
 }
 else{
 lcd.print("PM");
 }
}

void printDigits(int digits)
{
 if(digits < 10)
 lcd.print('0');
 lcd.print(digits);
}
  • This function is to display the current time on LCD display. This function implements LCD display function which is from LiquidCrystal.h library.
  • Available function from Time.h library:
    • year() – returns the value of current year
    • month() – returns the value of current month
    • day() – returns the value of current day
    • weekday() – returns the value of current week day
    • e.g. Sunday – 0, Monday – 1, etc.
    • hour() – returns the value of current hour in 24 format
    • hourFormat12() – returns value of current hour in 12 format
    • isAM() – returns true if it is AM
    • isPM() – returns true is it is PM
    • minute() – returns the value of current minute
    • second()- returns the value of current second
  • printDigits() function converts number into 2 digits display.
  • User can even display the specific details of past time or future time. For example:

time_t tim = now()+3600; //3600 seconds

printDigits(hour(tim));

User will find that the display time will be 1 hour past the            current time.


Time Change Settings
 Program Program description
void setup_Time()
{ 
 byte lcd_hpos[7]={0,3,6,11,14,14};
 byte lcd_vpos[7]={0,0,0,0,0,1};
 int lcd_pos = 0;
 int cur_time=millis();
 int prev_time=millis();
 boolean display_stat;
 boolean time_update = 0;
 int localKey;
 Time_Display();
while(1)
 {
 int sec_update = second();
 int min_update = minute();
 int hour_update = hour();
 int day_update = day();
 int month_update = month();
 int year_update = year();
 localKey = keypad.getKey();
 cur_time = millis();
 if(cur_time-prev_time>400)
 {
 display_stat = !display_stat;
 prev_time = cur_time;
 }
 if(display_stat==true)
 {
 lcd.setCursor(lcd_hpos[lcd_pos],lcd_vpos[lcd_pos]);
 lcd.print(" ");
 }
 else
 {
 Time_Display();
 } 
 if(localKey==Select)
 {
 if(time_update==true) 
  setTime(hour(),minute(),0,day(),month(),year());
 RTC.set(now());
 msg("UPDATING TIME");
 }
 else if(localKey==Left)
 {
 if(--lcd_pos<0) lcd_pos = 5;
 delay(pb_delay);
 }
 else if(localKey==Right)
 {
 if(++lcd_pos>5) lcd_pos = 0;
 delay(pb_delay);
 }
 else if(localKey==Up)
 {
 switch(lcd_pos)
 {
 case 0: day_update++; if(day_update>31) day_update=1;break;
 case 1: month_update++; if(month_update>12) month_update =1;break;
 case 2: year_update++; if(year_update>2100) year_update = 2013;break;
 case 3: hour_update++;
 if(isAM()==1){if(hour_update>11) hour_update =0;}
 else if(isPM()==1){if(hour_update>23) hour_update =12;}
 time_update = 1;break;
 case 4: min_update++;if(min_update>59) min_update =0;
 time_update = 1;break;
 case 5: if(isAM()==1)hour_update+=12;else hour_update-=12;break;
 }
 setTime(hour_update,min_update,sec_update,day_update,
 month_update,year_update);
 Time_Display();
 delay(pb_delay);
 }
 else if(localKey==Down)
 {
 switch(lcd_pos)
 {
 case 0: day_update--;if(day_update<1) day_update = 31;break;
 case 1: month_update--;if(month_update<1) month_update = 12;break;
 case 2: year_update--; if(year_update<2013) year_update = 2013;break;
 case 3: hour_update--; 
 if(isAM()==1){if(hour_update<0) hour_update =11;}
 else if(isPM()==1) {if(hour_update<12) hour_update =23;}
 time_update = 1;break;
 case 4: min_update--;if(min_update<0) min_update =59;time_update = 1;break;
 case 5: if(isAM()==1) hour_update+=12; else hour_update-=12;break;
 }
 setTime(hour_update,min_update,second(),day_update,
 month_update,year_update);
 Time_Display();
 delay(pb_delay);
 }
 }
}
  • This function is to change and set the current time and update the time to RTC module. This function implements LCD display function and LCD navigation buttons from LCD_Key.h library.
  • In this program, left and right buttons are used to switch to specific type like day, month,year,hour,minute and AM/PM. Up and down buttons are used to change the value. Select button is to update the set time to RTC module.
  • Highlighted part is to blink the time type user current choose to change and serves as indicator
  • pb_delay is delay time set. Here pb_delay = 200;
iv) MENU INTERFACE
In this project we also create a menu interface for fingerprint teaching process and time setup and it requires navigation buttons on LCD Keypad Shield.
Below is the sample code of a simple interface.
 Program Program description
//global variables for admin menu and interface
byte choice[2];
byte page_no;

void admin()
{
 int category = 0;
 int page = 0;
 int localKey;
 selection(category,page);// display first page
while(1)
 {
if(reset_)
 {
  category-=1;
  page = choice[category];
  selection(category,page);
  reset_=false;
 }
localKey = keypad.getKey();
 switch(localKey)
 {
 case Left: page--;
 if(page<0) page = 0;
 else if(page>page_no-1) page = page_no-1;
 selection(category,page);
 delay(200);
 break;
 case Right: page++;
 if(page<0) page = 0;
 else if(page>page_no-1) page = page_no-1;
 selection(category,page);
 delay(200);
 break;
 case Select:
 choice[++category] = page;
 choice[category-1] = choice[category];
 lcd.clear();
 selection(category,page);
 delay(200);
 break;

 case Up: category--;
 if(category<0)
 {
 lcd.clear();
 return;
 }
 else
 page = choice[category];
 lcd.clear();
 selection(category,page);
 delay(200);
 break;
 }
 }
}
void selection(int category,int page){
switch(category)
 {
 case 0: 
 page_no = 5;//set pages for first category
 menu(page);
 break;
 case 1:
 if(choice[category]==0){delay(pb_delay);//add function here} 
 else if(choice[category]==1){delay(pb_delay);//add function here}
 else if(choice[category]==2){delay(pb_delay);//add function here}
 else if(choice[category]==3){delay(pb_delay);//add function here}
 else if(choice[category]==4){delay(pb_delay);//add function here}
 //user can add more function here
reset_ = true;
 break; 
 }
}
void menu(int page){
lcd.clear();
 lcd.setCursor(0,0);
 lcd.print("Menu ");
 lcd.print(page+1);lcd.print(":");
switch(page)
 {
 case 0: //customise first page// break;
 case 1: //customise second page// break;
 case 2: //customise third page// break; 
 case 3: //customise forth page// break;
 case 4: //customise fifth page// break; 
 //user can add more here
 }
}
  • To use this interface, user just need to know how many options that needed to be created in the menu. Let say if 4 options are needed, change the value of page_no in void selection() to 4. Page_no determines how many options you want to set in the menu.
  • In void menu(), it is for user to design the page for each menu option. The most common way is to display the title of the menu.
  • In void selection() under case 1, user can add extra functions that dedicated to choice[category], like “else if(choice[category]==5){delay(pb_delay);//add function here}”. choice[category] is the menu option you selected, if you choose first menu option, choice[category] will be 0, then you can execute whatever function you want in this menu.
All the codes above are just part of the full source code and are taken as guidance for user to learn how the system works. In order for whole project to run, you can download sample full source code at the end of this tutorial.
4.0 Nearly finish...
After you have finished programming and everything else, just plug in DC adapter (to DC plug you soldered, not that from Arduino main board) and turn on the power. 
If you are not sure whether the power system works, just use multimeter to measure the voltage at pin 5V. The voltage should be 5V.
If everything is on, you are not done yet. Fingerprint and RTC module are new and you need to do something. As for RTC module, you will find that the date shown on LCD display will be different from current time ( should be 01-01-00). All you need to to do is go to time setup (I believe you have made a way to reach that menu and it is advisable to remove any verification process first for easy setup) and set up your time. If you have done this before finishing this project, then just skip it.
Fingerprint module now is free of data (except for its own ID and logo). You can teach it to read and store fingerprints or any information you want to store in the module.
If you are interested, you can make a container for the system. Nicer look and more 'professional'! The container we are using is the unwanted power supply casing.
Now your attendance system is ready to serve!!!
IMG_20130829_143519

5.0 Precautions
After you run the system for a while, you will find that the power system is quite warm. Don't worry too much about the heat, components can withstand it. But of course don't bake up the whole thing by putting it in warm place or in a small closed container. Getting the board too hot will probably damage the thing and resetting the system rapidly. Put the system in cool place or place with open air.
That's all! Hope you can enjoy it to develop your own attendance record system. There are so much more to learn and explore its possible potentials. Have fun! 8-) 
Attachment:
- Attendance System

Buy

, , , ,

Related Post

How to Make your Line Following Robot Faster?

Control Mobile Robot through WiFi with ESP-WiFi Shield (Arduino + Andriod)

Arduino + 2A motor + Encoder

Arduino + 2A Motor Shield + Encoder Motor

ESPresoo Lite V2 Package

Bringing IoT to Everyone – Collaboration of ASEAN Makers

11 thoughts on “Attendance Record System

  1. hye! im doing the same project for my class subject project.
    i had set up the same things but not using buzzer and ethernet and RTC ds1307. can i use the same coding here? i have compiled but there are some errors

    attsys:72: error: ‘Time_t’ was not declared in this scope
    attsys:62: error: ‘LCD_Key’ does not name a type
    attsys:67: error: ‘SdFat’ does not name a type
    attsys:70: error: ‘LCD_Key’ does not name a type
    attsys.ino: In function ‘void setup()’:
    attsys:105: error: ‘RTC’ was not declared in this scope
    attsys:105: error: ‘setSyncProvider’ was not declared in this scope
    attsys:108: error: ‘now’ was not declared in this scope
    attsys:111: error: ‘month’ was not declared in this scope
    attsys:112: error: ‘year’ was not declared in this scope
    attsys.ino: In function ‘void loop()’:
    attsys:117: error: ‘keypad’ was not declared in this scope
    attsys.ino: In function ‘boolean _respond(byte)’:
    attsys:210: error: ‘keypad’ was not declared in this scope
    attsys.ino: In function ‘void attendance()’:
    attsys:430: error: ‘worker_stat’ was not declared in this scope
    attsys:432: error: ‘now’ was not declared in this scope
    attsys.ino: In function ‘void Time_Display()’:
    attsys:448: error: ‘weekday’ was not declared in this scope
    attsys:452: error: ‘day’ was not declared in this scope
    attsys:454: error: ‘month’ was not declared in this scope
    attsys:456: error: ‘year’ was not declared in this scope
    attsys:460: error: ‘hourFormat12’ was not declared in this scope
    attsys:462: error: ‘minute’ was not declared in this scope
    attsys:467: error: ‘isAM’ was not declared in this scope
    attsys.ino: In function ‘void setup_Time()’:
    attsys:500: error: ‘second’ was not declared in this scope
    attsys:501: error: ‘minute’ was not declared in this scope
    attsys:502: error: ‘hour’ was not declared in this scope
    attsys:503: error: ‘day’ was not declared in this scope
    attsys:504: error: ‘month’ was not declared in this scope
    attsys:505: error: ‘year’ was not declared in this scope
    attsys:507: error: ‘keypad’ was not declared in this scope
    attsys:528: error: ‘setTime’ was not declared in this scope
    attsys:529: error: ‘RTC’ was not declared in this scope
    attsys:529: error: ‘now’ was not declared in this scope
    attsys:555: error: ‘isAM’ was not declared in this scope
    attsys:556: error: ‘isPM’ was not declared in this scope
    attsys:560: error: ‘isAM’ was not declared in this scope
    attsys:563: error: ‘setTime’ was not declared in this scope
    attsys:576: error: ‘isAM’ was not declared in this scope
    attsys:577: error: ‘isPM’ was not declared in this scope
    attsys:580: error: ‘isAM’ was not declared in this scope
    attsys:583: error: ‘setTime’ was not declared in this scope
    attsys.ino: At global scope:
    attsys:591: error: redefinition of ‘String showTime’
    attsys:72: error: ‘String showTime’ previously declared here
    attsys:591: error: ‘Time_t’ was not declared in this scope
    attsys.ino: In function ‘void record(String)’:
    attsys:624: error: ‘sd’ was not declared in this scope
    attsys:634: error: ‘sd’ was not declared in this scope
    attsys:642: error: no matching function for call to ‘SdFile::open(char [(((unsigned int)((int)fileName.String::length())) + 1)], int)’
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:223: note: candidates are: uint8_t SdFile::open(SdFile*, uint16_t, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:224: note: uint8_t SdFile::open(SdFile*, const char*, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:331: note: uint8_t SdFile::open(SdFile&, const char*, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:335: note: uint8_t SdFile::open(SdFile&, const char*)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:341: note: uint8_t SdFile::open(SdFile&, uint16_t, uint8_t)
    attsys:647: error: no matching function for call to ‘SdFile::open(char [(((unsigned int)((int)fileName.String::length())) + 1)], int)’
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:223: note: candidates are: uint8_t SdFile::open(SdFile*, uint16_t, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:224: note: uint8_t SdFile::open(SdFile*, const char*, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:331: note: uint8_t SdFile::open(SdFile&, const char*, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:335: note: uint8_t SdFile::open(SdFile&, const char*)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:341: note: uint8_t SdFile::open(SdFile&, uint16_t, uint8_t)
    attsys:670: error: ‘worker_stat’ was not declared in this scope
    attsys.ino: In function ‘void wr_log(boolean)’:
    attsys:681: error: ‘sd’ was not declared in this scope
    attsys:691: error: ‘sd’ was not declared in this scope
    attsys:699: error: no matching function for call to ‘SdFile::open(const char [8], int)’
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:223: note: candidates are: uint8_t SdFile::open(SdFile*, uint16_t, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:224: note: uint8_t SdFile::open(SdFile*, const char*, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:331: note: uint8_t SdFile::open(SdFile&, const char*, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:335: note: uint8_t SdFile::open(SdFile&, const char*)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:341: note: uint8_t SdFile::open(SdFile&, uint16_t, uint8_t)
    attsys:708: error: no matching function for call to ‘SdFile::open(const char [8], const uint8_t&)’
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:223: note: candidates are: uint8_t SdFile::open(SdFile*, uint16_t, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:224: note: uint8_t SdFile::open(SdFile*, const char*, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:331: note: uint8_t SdFile::open(SdFile&, const char*, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:335: note: uint8_t SdFile::open(SdFile&, const char*)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:341: note: uint8_t SdFile::open(SdFile&, uint16_t, uint8_t)
    attsys:725: error: ‘class SdFile’ has no member named ‘fgets’
    attsys:733: error: ‘now’ was not declared in this scope
    attsys:738: error: ‘now’ was not declared in this scope
    attsys.ino: In function ‘char* rd_log(boolean)’:
    attsys:755: error: ‘sd’ was not declared in this scope
    attsys:766: error: no matching function for call to ‘SdFile::open(const char [8], const uint8_t&)’
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:223: note: candidates are: uint8_t SdFile::open(SdFile*, uint16_t, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:224: note: uint8_t SdFile::open(SdFile*, const char*, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:331: note: uint8_t SdFile::open(SdFile&, const char*, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:335: note: uint8_t SdFile::open(SdFile&, const char*)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:341: note: uint8_t SdFile::open(SdFile&, uint16_t, uint8_t)
    attsys:779: error: ‘class SdFile’ has no member named ‘fgets’
    attsys.ino: In function ‘void rdAdmin()’:
    attsys:799: error: ‘sd’ was not declared in this scope
    attsys:808: error: no matching function for call to ‘SdFile::open(const char [10], const uint8_t&)’
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:223: note: candidates are: uint8_t SdFile::open(SdFile*, uint16_t, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:224: note: uint8_t SdFile::open(SdFile*, const char*, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:331: note: uint8_t SdFile::open(SdFile&, const char*, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:335: note: uint8_t SdFile::open(SdFile&, const char*)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:341: note: uint8_t SdFile::open(SdFile&, uint16_t, uint8_t)
    attsys:808: error: no matching function for call to ‘SdFile::open(const char [12], const uint8_t&)’
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:223: note: candidates are: uint8_t SdFile::open(SdFile*, uint16_t, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:224: note: uint8_t SdFile::open(SdFile*, const char*, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:331: note: uint8_t SdFile::open(SdFile&, const char*, uint8_t)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:335: note: uint8_t SdFile::open(SdFile&, const char*)
    C:\Program Files (x86)\Arduino\libraries\SD/utility/SdFat.h:341: note: uint8_t SdFile::open(SdFile&, uint16_t, uint8_t)
    attsys.ino: In function ‘boolean checkSDCard()’:
    attsys:855: error: ‘class SdVolume’ has no member named ‘freeClusterCount’
    attsys.ino: In function ‘void dateTime(uint16_t*, uint16_t*)’:
    attsys:867: error: ‘year’ was not declared in this scope
    attsys:867: error: ‘month’ was not declared in this scope
    attsys:867: error: ‘day’ was not declared in this scope
    attsys:869: error: ‘hour’ was not declared in this scope
    attsys:869: error: ‘minute’ was not declared in this scope
    attsys:869: error: ‘second’ was not declared in this scope
    attsys.ino: In function ‘void admin()’:
    attsys:893: error: ‘keypad’ was not declared in this scope
    attsys.ino: In function ‘void add_fprint()’:
    attsys:992: error: ‘keypad’ was not declared in this scope
    attsys.ino: In function ‘void del_fprint()’:
    attsys:1097: error: ‘keypad’ was not declared in this scope
    attsys.ino: In function ‘void empty_database()’:
    attsys:1155: error: ‘keypad’ was not declared in this scope
    attsys.ino: In function ‘boolean password_check()’:
    attsys:1232: error: ‘keypad’ was not declared in this scope

    can you help me? thanks!

  2. Can someone help with these kindly???

    Read flash
    Write flash
    download fingerprint template
    upload fingerprint template

    Give an example code for arduino!

  3. I have a problem with LCD keypad,
    The buttons/keypad is no respons.

    I use LCD Keypad DFRobot type.

    Pls advice.
    Thnx

  4. Hi Cytron,

    I can’t reach to the “menu” interface,
    when i press “Select” button, screen write “PLACE FINGER ON SCREEN” and then “ACCESS Denied”

    Could you help to make a simple user manual for operate and reach admin interface in this device?

    Kindly send to my email.

    Thank you,

  5. Hi Januar P,

    Just comment the following line in void admin() to remove fingerprint verification and proceed.

    if(!admin_fpcheck()) return;

    This line can only works if you have added the fingerprint to the database with the name “admin”.

    You can also try out the password security to enter admin menu instead of fingerprint verification with password_check function.

    Thanks.

  6. Hello Cytron,

    Before buying this hardware/component here, I want to try running using simulation software. So my question is, what software should I use to construct the circuit and run the coding?

  7. Hello Cytron,

    What type of database is used and if it save to the sdcard, what application can it be view on?

    Thanks.

  8. Hi Cytron,

    I’m doing a project with similar system as this one. Using the power system provided by this page, I noticed that my voltage regulator, LM7805 heating so hot to the degree that I cannot touch it. Is that normal?

    Thanks.

Leave a Reply

Your email address will not be published. Required fields are marked *