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 Mega board, SM630 fingerprint module, Breakout Board Micro SD and other devices such as LCD display shield and RTC (Real Time Clock).

1.1 Feature

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

DSC09042

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 Breakout Board Micro SD  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:

RTC Module System:

  • 1 x Arduino Type Header pin

BreakoutBroad Micro SD:

  • 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.

 

DSC09108

Befor stacking up, we need built some circiut on Prototyping  Shield as shown below.

DSC09112

 

We need build a 10K Resistor on SDI and built a 10K resistor on SDO to decrease the voltage value at Breakout Board Micro SD because it can make the Breakout Board Micro SD don’t overheat.

  DSC09109DSC09114

 

 

 

 

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.

DSC09043DSC09113

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.

DSC09111DSC09115

 

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

 

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 ascommand. After the device receives the data bytes, it will send back a string of bytes in serial asresponse. 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
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 variableworker_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 initialize the card and vol.init is to initialize 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. FunctiondateTimeCallback(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, useO_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
}
void SET_rtc() {
}
bool getTime(const char *str)
{
int Hour, Min, Sec;
if (sscanf(str, "%d:%d:%d", &Hour, &Min, &Sec) != 3) return false;
tm.Hour = Hour;
tm.Minute = Min;
tm.Second = Sec;
return true;
}
bool getDate(const char *str)
{
char Month[12];
int Day, Year;
uint8_t monthIndex;
if (sscanf(str, "%s %d %d", Month, &Day, &Year) != 3) return false;
for (monthIndex = 0; monthIndex < 12; monthIndex++) {
if (strcmp(Month, monthName[monthIndex]) == 0) break;
}
if (monthIndex >= 12) return false;
tm.Day = Day;
tm.Month = monthIndex + 1;
tm.Year = CalendarYrToTm(Year);
return true;
}
}
  • 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.
  • In void RTC set(),it is reset time.
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
-Library

Buy

Leave a Reply

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