I have been given a task to build an attendance system for our staff. The requirements are quite straight-forward. It needs to be standalone (without computer), fingerprint recognition is preferred and it should record real time into an 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. However your perception might change after you read this article. In this tutorial, we are going to demonstrate how to make a simple attendance system using currently on-sale Cytron products including the Arduino Mega, SM630 fingerprint module, Breakout Board Micro SD and other devices such as LCD display shield and an RTC Module (Real Time Clock).
1.1 Feature
The objective of this tutorial is to build a 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 through Breakout Board Micro SD to store attendance information that 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) as navigation buttons for the user.
- includes RTC module to provide current date and time.
- includes other devices such as a buzzer as indicator.
2.0 Getting Started
2.1 Components
The components you need:
- 1 x Arduino Mega 2560 R3-Main Board
- 1 x Fingerprint Reader Integrated SM630
- 1 x LCD keypad Shield
- 1 x DS3231 RTC Module
- 1 x Breakout Board Micro SD
- 1 x Micro SD card, you can get it from any computer store.
- 1 x Cytron Prototyping Shield (for buzzer, UART and power system)
- 1 x Cytron Ethernet Shield
Electronic components:
Indicator:
- 1 x Buzzer-PCB Mount
- 2x Resistor 10K
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.
Before stacking up, we need build a circuit consists of all necessary systems on prototyping shield.
2.22 Indicator
We use a buzzer to indicate when an attendance is successful recorded. One beep indicates login success, 2 beeps means logout and 3 beep sounds indicates an error, perhaps the improper installation of microSD card or an unrecognized employee fingerprint, etc. If you use our example code, connect longer leg of the buzzer to A1 and another to GND.
User can also customize their own indicator systems such as using visual indicators like LEDs.
2.23 Fingerprint Module UART Connection
The figure below shows the connection between the connector and the Arduino prototyping shield.

2.24 RTC module
Users 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 all the information stored in the RTC and reset its time. Handle with care!
We are going to use 4 pins only :VCC, SCL, SDA and GND. Leave others as unconnected.
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.
Connect the SDA wire to pin 20 and SCL to pin21. There should labels for you as guidance.
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.
Basically the programming involves 4 major parts:
- Fingerprint scanning process and teaching process
- Information storage and access with the fingerprint module and micro SD,
- Time settings and display
- Menu interface for time setup, fingerprint teaching ,etc.
i) FINGERPRINT SCANNING AND TEACHING
The SM630 Fingerprint module uses packet data communication. If we wants the module to carry out certain operations, we need to send a command – which is a string of bytes in serial. After the device receives the data, it will send back a response – which is another string of bytes in serial. It is our job to program the board to ‘write’ and ‘read’ these bytes and its following actions.
Generally the communication packet includes the following:
- packet head – 2 bytes
- packet flag – 1 byte
- packet length – 1 byte
- packet content – N bytes (any number of 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 FP_DETECTED 0x32
- #define TIME_OUT 0x33
- #define PROCESS_FAILED 0x34
- #define PARAMETER_ERR 0x35
- #define MATCH 0x37
- #define NO_MATCH 0x38
- #define FP_FOUND 0x39
- #define FP_UNFOUND 0x3A
Sending command
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); }
|
|
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) { if(time_on) Time_Display(); if(keypad.getKey()==Select) { key_status = true; Serial.end(); return success; } } resp_temp[j]=Serial.read(); if(j==3) packet_length = resp_temp[3]+5; } byte response[packet_length]; for(byte j=0;j<packet_length;j++) response[j]=resp_temp[j]; worker_id = -1; //if(response[4]==0x02&&cmd==SEARCH_FINGERPRINT) return 0;// switch(response[5]) { case OP_SUCCESS: if(cmd==SEARCH_FINGERPRINT) {msg("SEARCHING...");} else if(cmd==ADD_FINGERPRINT) msg("ADDING..."); else if(cmd==DELETE_FINGERPRINT) { msg("DELETING..."); end_flag = true; success = true; } else if(cmd==EMPTY_DATABASE) { end_flag=true; success = true; } if(cmd==byte(ADD_FINGERPRINT)) addfp_count++; break; case FP_DETECTED: msg("FPRINT DETECTED"); success = true; break; case TIME_OUT: //msg("TIME OUT"); if(time_on) { lcd.clear(); lcd.setCursor(5, 0);lcd.print("Cytron"); lcd.setCursor(2, 1);lcd.print("Technologies"); delay(1000);lcd.clear(); } if(!time_on) beep(3); end_flag=true; break; case PROCESS_FAILED: beep(3); msg("PROCESS FAILED"); end_flag=true; break; case PARAMETER_ERR: beep(3); msg("PARAMETER ERR"); end_flag=true; break; case MATCH: //msg("FPRINT MATCHED"); success = true; end_flag=true; break; case NO_MATCH: //msg("FPRINT UNMATCH"); end_flag=true; worker_id = 0; break; case FP_FOUND: //msg("FPRINT FOUND"); worker_id = response[6]*256+response[7]; end_flag=true; success = true; break; case FP_UNFOUND: if(!A) { beep(3); msg("FPRINT UNFOUND"); } end_flag=true; worker_id = -2; break; } if(cmd==byte(ADD_FINGERPRINT)&&addfp_count>1) { success = true; end_flag=true; } } Serial.end(); return success; }
|
|
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) {msg("RECEIVE ERR");return;} char ops_[7]; while(Serial.available()==0){} Serial.readBytes(ops_,7); if(ops_[5]!=OP_SUCCESS) {msg("WR FAILED");return;} Serial.end(); }
|
|
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)continue; Serial.readBytes(rx,6); if(rx[3]==0x01&&rx[4]==0x02) {msg("RECEIVE ERR");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; }
|
|
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, include the latest SD library in your Arduino library directory. The file can be found and downloaded at the end of this tutorial.
#include <SdFat.h> #define chipSelect 4 //CS pin for microSD Sd2Card card; SdVolume vol; SdFat sd;
Below are the sample codes for accessing,writing and reading data from micro SD.
Checking micro SD condition
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 (although it is quite impossible to get full memory) if(freespace<10000) msg("MEMORY FULL"); else if(freespace<0.1*volumesize) msg("MEMORY NEAR FULL"); return true; }
|
|
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,return if faulty 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,"); 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(); } }
|
|
Read file from microSD
Program | Program description |
void rdAdmin() { SdFile dataFile; if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { if(!checkSDCard()) { msg("RD ADMIN FAIL"); while(!checkSDCard()) continue; } } if (!(dataFile.open("admin.csv", O_READ)||dataFile.open("admin~1.csv", O_READ))) { msg("RD ADMIN FAIL"); return; } char data; String worker_data[3]; 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) { 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]); //wrFlash((worker_data[0].toInt()),STAT,"0"); } line_count++; count = 0; memset(worker_data,0,sizeof(worker_data)); } } dataFile.close(); }
|
|
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 rdAdmin() { SdFile dataFile; if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { if(!checkSDCard()) { msg("RD ADMIN FAIL"); while(!checkSDCard()) continue; } } if (!(dataFile.open("admin.csv", O_READ)||dataFile.open("admin~1.csv", O_READ))) { msg("RD ADMIN FAIL"); return; } char data; String worker_data[3]; 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) { 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]); //wrFlash((worker_data[0].toInt()),STAT,"0"); } line_count++; count = 0; memset(worker_data,0,sizeof(worker_data)); } } dataFile.close(); }
|
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"); fileName = String(Month[month()-1])+String("_")+String(year()%100)+String(".csv"); break; } 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); } } }
|
|
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 |
/////////////////////////////Admin & Interface/////////////////////////////// void admin() { int category = 0; int option = 0; int prev_page = 0; int page = 0; int localKey; //if(!password_check()) return; // if(!admin_fpcheck()) return; while(1) { if(reset_) { category-=1; page = prev_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,option,page); delay(200); break; case Right: page++; if(page<0) page = 0; else if(page>page_no-1) page = page_no-1; selection(category,option,page); delay(200); break; case Select: category++; option = page; prev_page = page; page=0; lcd.clear(); selection(category,option,page); delay(200); break; case Up: category--; if(category<0) { //category = 0; lcd.clear(); return; } else page = prev_page; lcd.clear(); selection(category,option,page); delay(200); break; case None: selection(category,option,page); delay(200); break; } } } void selection(int category,int option,int page){ choice[category] = option; 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_fprint();} else if(choice[category]==1){delay(pb_delay);del_fprint();} else if(choice[category]==2){delay(pb_delay);empty_database();} else if(choice[category]==3){delay(pb_delay);setup_Time();} else if(choice[category]==4){delay(pb_delay);rdAdmin();} 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: lcd.setCursor(6,1);lcd.print("ADD FPRINT");break; case 1: lcd.setCursor(6,1);lcd.print("DEL FPRINT");break; case 2: lcd.setCursor(2,1);lcd.print("EMPTY DATABASE");break; case 3: lcd.setCursor(6,1);lcd.print("TIME SETUP");break; case 4: lcd.setCursor(5,1);lcd.print("UPDATE LIST");break; //user can customize or add more pages too } } 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; }
|
|
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!!!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!
Attachment: