Fingerprint Scanner API using GT511C3 fingerprint scanner.

Dependencies:   4DGL-uLCD-SE GT511C3 SDFileSystem mbed

Fork of GT511C3test by Toshihisa T

The fingerprint scanner is designed to take attendance over a group of students. It requires a the group owner to store a preloaded list of student id numbers in a .txt file to the memory (SD card) in return of a 5 digits keypass to gain access to the database when taking attendance.

While there may exist multiple group owner and a group owner with multiple databases, each group will be uniquely identified by the 5 digits keypass. The program limits each scanner to open ONE session at a time where only one group will be able to take attendance during its session. Once a session is closed, a report of the attendance taken during the open session is generated and sent via ethernet to owner and there is no way to reopen the session again.

For the initial setup, each fingerprint database needs to be populated by the students. This set up can be done continuously during a session while taking attendance that session.

Files at this revision

API Documentation at this revision

Comitter:
yoshua0207
Date:
Tue Dec 01 19:10:10 2015 +0000
Parent:
7:8b9ef3211cd0
Commit message:
Current fingerprint scanner code.

Changed in this revision

4DGL-uLCD-SE.lib Show annotated file Show diff for this revision Revisions of this file
SDFileSystem.lib Show annotated file Show diff for this revision Revisions of this file
defs.h Show annotated file Show diff for this revision Revisions of this file
i2c.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mpr121.h Show annotated file Show diff for this revision Revisions of this file
types.h Show annotated file Show diff for this revision Revisions of this file
diff -r 8b9ef3211cd0 -r a1ba925cf903 4DGL-uLCD-SE.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/4DGL-uLCD-SE.lib	Tue Dec 01 19:10:10 2015 +0000
@@ -0,0 +1,1 @@
+https://mbed.org/users/4180_1/code/4DGL-uLCD-SE/#e39a44de229a
diff -r 8b9ef3211cd0 -r a1ba925cf903 SDFileSystem.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem.lib	Tue Dec 01 19:10:10 2015 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/SDFileSystem/#c8f66dc765d4
diff -r 8b9ef3211cd0 -r a1ba925cf903 defs.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/defs.h	Tue Dec 01 19:10:10 2015 +0000
@@ -0,0 +1,86 @@
+/*! \file avrlibdefs.h \brief AVRlib global defines and macros. */
+//*****************************************************************************
+//
+// File Name    : 'avrlibdefs.h'
+// Title        : AVRlib global defines and macros include file
+// Author       : Pascal Stang
+// Created      : 7/12/2001
+// Revised      : 9/30/2002
+// Version      : 1.1
+// Target MCU   : Atmel AVR series
+// Editor Tabs  : 4
+//
+//  Description : This include file is designed to contain items useful to all
+//                  code files and projects, regardless of specific implementation.
+//
+// This code is distributed under the GNU Public License
+//      which can be found at http://www.gnu.org/licenses/gpl.txt
+//
+//*****************************************************************************
+
+
+#ifndef AVRLIBDEFS_H
+#define AVRLIBDEFS_H
+
+//#define F_CPU 4000000
+#define MEM_TYPE 1
+
+// Code compatibility to new AVR-libc
+// outb(), inb(), inw(), outw(), BV(), sbi(), cbi(), sei(), cli()
+#ifndef outb
+    #define outb(addr, data)    addr = (data)
+#endif
+#ifndef inb
+    #define inb(addr)           (addr)
+#endif
+#ifndef outw
+    #define outw(addr, data)    addr = (data)
+#endif
+#ifndef inw
+    #define inw(addr)           (addr)
+#endif
+#ifndef BV
+    #define BV(bit)         (1<<(bit))
+#endif
+//#ifndef cbi
+//  #define cbi(reg,bit)    reg &= ~(BV(bit))
+//#endif
+//#ifndef sbi
+//  #define sbi(reg,bit)    reg |= (BV(bit))
+//#endif
+#ifndef cli
+    #define cli()           __asm__ __volatile__ ("cli" ::)
+#endif
+#ifndef sei
+    #define sei()           __asm__ __volatile__ ("sei" ::)
+#endif
+
+// support for individual port pin naming in the mega128
+// see port128.h for details
+#ifdef __AVR_ATmega128__
+// not currently necessary due to inclusion
+// of these defines in newest AVR-GCC
+// do a quick test to see if include is needed
+#ifndef PD0
+    //#include "port128.h"
+#endif
+#endif
+
+// use this for packed structures
+// (this is seldom necessary on an 8-bit architecture like AVR,
+//  but can assist in code portability to AVR)
+#define GNUC_PACKED __attribute__((packed)) 
+
+// port address helpers
+#define DDR(x) ((x)-1)    // address of data direction register of port x
+#define PIN(x) ((x)-2)    // address of input register of port x
+
+// MIN/MAX/ABS macros
+#define MIN(a,b)            ((a<b)?(a):(b))
+#define MAX(a,b)            ((a>b)?(a):(b))
+#define ABS(x)              ((x>0)?(x):(-x))
+
+// constants
+#define PI      3.14159265359
+
+#endif
diff -r 8b9ef3211cd0 -r a1ba925cf903 i2c.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/i2c.h	Tue Dec 01 19:10:10 2015 +0000
@@ -0,0 +1,190 @@
+//      This library provides the high-level functions needed to use the I2C
+//  serial interface supported by the hardware of several AVR processors.
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include "types.h"
+#include "defs.h"
+
+// TWSR values (not bits)
+// (taken from avr-libc twi.h - thank you Marek Michalkiewicz)
+// Master
+#define TW_START                    0x08
+#define TW_REP_START                0x10
+// Master Transmitter
+#define TW_MT_SLA_ACK               0x18
+#define TW_MT_SLA_NACK              0x20
+#define TW_MT_DATA_ACK              0x28
+#define TW_MT_DATA_NACK             0x30
+#define TW_MT_ARB_LOST              0x38
+// Master Receiver
+#define TW_MR_ARB_LOST              0x38
+#define TW_MR_SLA_ACK               0x40
+#define TW_MR_SLA_NACK              0x48
+#define TW_MR_DATA_ACK              0x50
+#define TW_MR_DATA_NACK             0x58
+// Slave Transmitter
+#define TW_ST_SLA_ACK               0xA8
+#define TW_ST_ARB_LOST_SLA_ACK      0xB0
+#define TW_ST_DATA_ACK              0xB8
+#define TW_ST_DATA_NACK             0xC0
+#define TW_ST_LAST_DATA             0xC8
+// Slave Receiver
+#define TW_SR_SLA_ACK               0x60
+#define TW_SR_ARB_LOST_SLA_ACK      0x68
+#define TW_SR_GCALL_ACK             0x70
+#define TW_SR_ARB_LOST_GCALL_ACK    0x78
+#define TW_SR_DATA_ACK              0x80
+#define TW_SR_DATA_NACK             0x88
+#define TW_SR_GCALL_DATA_ACK        0x90
+#define TW_SR_GCALL_DATA_NACK       0x98
+#define TW_SR_STOP                  0xA0
+// Misc
+#define TW_NO_INFO                  0xF8
+#define TW_BUS_ERROR                0x00
+
+// defines and constants
+#define TWCR_CMD_MASK       0x0F
+#define TWSR_STATUS_MASK    0xF8
+
+// return values
+#define I2C_OK              0x00
+#define I2C_ERROR_NODEV     0x01
+
+#define sbi(var, mask)   ((var) |= (uint8_t)(1 << mask))
+#define cbi(var, mask)   ((var) &= (uint8_t)~(1 << mask))
+
+#define WRITE_sda() DDRC = DDRC | 0b00010000 //SDA must be output when writing
+#define READ_sda()  DDRC = DDRC & 0b11101111 //SDA must be input when reading - don't forget the resistor on SDA!!
+
+// functions
+
+//! Initialize I2C (TWI) interface
+void i2cInit(void);
+
+//! Set the I2C transaction bitrate (in KHz)
+void i2cSetBitrate(unsigned short bitrateKHz);
+
+// Low-level I2C transaction commands 
+//! Send an I2C start condition in Master mode
+void i2cSendStart(void);
+//! Send an I2C stop condition in Master mode
+void i2cSendStop(void);
+//! Wait for current I2C operation to complete
+void i2cWaitForComplete(void);
+//! Send an (address|R/W) combination or a data byte over I2C
+void i2cSendByte(unsigned char data);
+//! Receive a data byte over I2C  
+// ackFlag = TRUE if recevied data should be ACK'ed
+// ackFlag = FALSE if recevied data should be NACK'ed
+void i2cReceiveByte(unsigned char ackFlag);
+//! Pick up the data that was received with i2cReceiveByte()
+unsigned char i2cGetReceivedByte(void);
+//! Get current I2c bus status from TWSR
+unsigned char i2cGetStatus(void);
+void delay_ms(uint16_t x);
+
+// high-level I2C transaction commands
+
+//! send I2C data to a device on the bus (non-interrupt based)
+unsigned char i2cMasterSendNI(unsigned char deviceAddr, unsigned char length, unsigned char* data);
+//! receive I2C data from a device on the bus (non-interrupt based)
+unsigned char i2cMasterReceiveNI(unsigned char deviceAddr, unsigned char length, unsigned char *data);
+
+/*********************
+ ****I2C Functions****
+ *********************/
+
+void i2cInit(void)
+{
+    // set i2c bit rate to 40KHz
+    i2cSetBitrate(100);
+    // enable TWI (two-wire interface)
+    sbi(TWCR, TWEN);    // Enable TWI
+}
+
+void i2cSetBitrate(unsigned short bitrateKHz)
+{
+    unsigned char bitrate_div;
+    // set i2c bitrate
+    // SCL freq = F_CPU/(16+2*TWBR))
+    cbi(TWSR, TWPS0);
+    cbi(TWSR, TWPS1);
+    
+    //calculate bitrate division    
+    bitrate_div = ((F_CPU/4000l)/bitrateKHz);
+    if(bitrate_div >= 16)
+        bitrate_div = (bitrate_div-16)/2;
+    outb(TWBR, bitrate_div);
+}
+
+void i2cSendStart(void)
+{
+    WRITE_sda();
+    // send start condition
+    TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
+}
+
+void i2cSendStop(void)
+{
+    // transmit stop condition
+        TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
+}
+
+void i2cWaitForComplete(void)
+{
+    int i = 0;      //time out variable
+    
+    // wait for i2c interface to complete operation
+    while ((!(TWCR & (1<<TWINT))) && (i < 90))
+        i++;
+}
+
+void i2cSendByte(unsigned char data)
+{
+    delay_ms(1);
+    //printf("sending 0x%x\n", data);
+    WRITE_sda();
+    // save data to the TWDR
+    TWDR = data;
+    // begin send
+    TWCR = (1<<TWINT)|(1<<TWEN);
+}
+
+void i2cReceiveByte(unsigned char ackFlag)
+{
+    // begin receive over i2c
+    if( ackFlag )
+    {
+        // ackFlag = TRUE: ACK the recevied data
+        outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
+    }
+    else
+    {
+        // ackFlag = FALSE: NACK the recevied data
+        outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
+    }
+}
+
+unsigned char i2cGetReceivedByte(void)
+{
+    // retieve received data byte from i2c TWDR
+    return( inb(TWDR) );
+}
+
+unsigned char i2cGetStatus(void)
+{
+    // retieve current i2c status from i2c TWSR
+    return( inb(TWSR) );
+}
+
+void delay_ms(uint16_t x)
+{
+  uint8_t y, z;
+  for ( ; x > 0 ; x--){
+    for ( y = 0 ; y < 90 ; y++){
+      for ( z = 0 ; z < 6 ; z++){
+        asm volatile ("nop");
+      }
+    }
+  }
+}
\ No newline at end of file
diff -r 8b9ef3211cd0 -r a1ba925cf903 main.cpp
--- a/main.cpp	Fri Jan 03 16:03:44 2014 +0000
+++ b/main.cpp	Tue Dec 01 19:10:10 2015 +0000
@@ -1,61 +1,394 @@
-#include "mbed.h"
 #include "GT511C3.hpp"
+#include "uLCD_4DGL.h"
+#include "SDFileSystem.h"
+#include <string>
 
+
+// LCD, IO
+uLCD_4DGL uLCD(p28, p27, p30);
 Serial debug(USBTX,USBRX);
+GT511C3 finger(p9,p10);
+SDFileSystem sd(p5, p6, p7, p8, "sd");
+
+// Set these variables to 0 after each read
+// KEYPAD_BOTTOM LEFT
+DigitalOut is_cancelled(LED1);//10
+// KEYPAD_BOTTOM_RIGHT
+DigitalOut is_entered(LED2); //11
 
-DigitalOut myled(LED1);
-GT511C3 finger(p28,p27);
+// FUNCTIONS:
+void user_menu();
+void open_existing_system();
+void start_session();
+void close_session();
+void read_from_fingerprint_scanner();
+void close_session();
+int read_keypad(char* readable, int how_many);
+void upload_to_scanner();
+void download_to_memory();
+void send_file_via_wifi();
+int owner_database_contains(char* gid);
+void wait_cancelled_or_entered();
 
-int progress(int status,char *msg)
-{
-    debug.printf("%s",msg);
+// CONSTANT VARIABLES
+const int id_size = 5;
+const int group_size = 200;
+const int owner_id_size = 20;
+string owner_id_file = "owner_id_file.txt";
+string sid_file = "sid_file.txt";
+
+// VARIABLES
+char group_id[id_size]; // group_id null >> no open session
+int this_session_id[group_size];
+int student_id_number[group_size];
+char* from_user;
+
+int main() {
+    user_menu();
     return 0;
 }
 
-int main() {
-    int sts = 0;
-    int ID = 0;
+void user_menu() {
+    
+    uLCD.printf("***********************\n");
+    uLCD.printf("* Fingerprint Scanner *\n");
+    uLCD.printf("***********************\n\n");
+    uLCD.printf("Welcome!\n\n");
+    open_existing_system();
+}
+
+/*
+ * Starts the program by asking the user which database
+ * the user is going to be using for the session.
+ */
+void open_existing_system() {
+    
+    int is_done = 0;
+    while (!is_done){
+        
+        //Ask for user input
+        uLCD.printf("Enter group ID: ");
+            
+        // get user input >> read group id ******
+        from_user = "41811";
+        
+        // check the input against owner data
+        if (owner_database_contains(from_user)) {
+            start_session();
+        }
+    }
+}
 
-    debug.format(8,Serial::None,1);
-    debug.baud(115200);
-
-    debug.printf("Fingerprint reader module \"GT-511C3 / GT-511C31\" test program.\n");
-    debug.printf("Build: %s %s\n",__DATE__,__TIME__);
+/*
+ * This method checks whether the database contains the specific group
+ * that the user specified using the id
+ * 
+ * @param char* gid user specified string id "****" of length id_size
+ * @return int 1 if true, 0 otherwise
+ */
+int owner_database_contains(char* gid) {
+    string fileName = "/sd/"  + owner_id_file;
+    const char* conv_file = fileName.c_str();
+    FILE *file = fopen(conv_file, "r");
+    debug.printf("Opening file %s \n",fileName);
+    if (file) {
+        char c;
+        char* temp[id_size];
+        c = fgetc(file);
+        while(c != "\n")
+        {
+            temp.putc(c);
+            c = fgetc(file);
+        }
+        debug.printf("temp is %s\n", temp);
+        while (temp) {
+            if (int(*gid) == int(*temp)) {
+                fclose(file);
+                return 1;
+            }
+            char c;
+            char temp[id_size];
+            c = fgetc(file);
+            while(c ~= "\n")
+            {
+                temp.putc(c);
+                c = fgetc(file);
+            }
+        }
 
-    debug.printf("Open\n");
-    sts = finger.Open();
-    debug.printf("sts = %d\n",sts);
-    if(sts == 0){
-        int i;
-        debug.printf("FirmwareVersion = %lx\n",finger.FirmwareVersion);
-        debug.printf("IsoAreaMaxSize = %ld\n",finger.IsoAreaMaxSize);
-        debug.printf("DeviceSerialNumber = ");
-        for(i = 0; i < sizeof(finger.DeviceSerialNumber);i++){
-            debug.printf("%02X",finger.DeviceSerialNumber[i]);
+    }
+    fclose(file);
+    return -1;
+}
+
+int sid_file_contains(char* sid) {
+    string fileName = "/sd/"  + string(group_id) + "/" + sid_file;
+    const char* conv_file = fileName.c_str();
+    FILE *file = fopen(conv_file, "r");
+    debug.printf("Opening file %s\n",fileName);
+    if (file) {
+        char temp[15];
+        file.getline(temp, 15);
+        while(temp)
+        {
+            digitC = strtok(temp, " ");
+            if(strcmp(digitC[1],sid))
+            {
+                fclose(file);
+                return (int)digitC[0];
+            }
+            file.getline(temp, 15);
         }
-        debug.printf("\n");
     }
+    fclose(file);
+    return -1;
+}
+
+/*
+ * Start session is called once a group database has been identified
+ * and loaded to the fingerprint scanner. Start session will allow
+ * users to start scanning fingerprints for the current session.
+ *
+ * @param: none
+ * @return: none
+ */
+void start_session() {
+    
+    // upload the specified database to the fingerprint
+    upload_to_scanner();
 
-    if(1){
-        int EnrollID = 11;
-        if(finger.CheckEnrolled(EnrollID) == 0){
-            debug.printf("EnrollID(%d) is already enrolled.Delete!\n",EnrollID);
-            if(finger.DeleteID(EnrollID) == 0){
-                debug.printf("Delete OK!\n");
+    // display options to user via LCD
+    uLCD.printf("Session <session_name> is open.\n");
+    uLCD.printf("Press (1) Scan fingerprint!\n\n");
+    uLCD.printf("Press (2) Add fingerprint!\n\n");
+    uLCD.printf("Press (11) to close session\n");
+    uLCD.printf("Press (10) to go back\n");
+    
+    int is_closed = 0;
+    while (!is_closed) {
+        
+//        int digit = get_digit_keypad();
+        int digit = 2;
+        if (digit = 1) {
+            // scan the fingerprint
+            read_from_fingerprint_scanner();
+        }
+        
+        if(digit = 2){
+            // add new fingerprints
+            add_new_fingerprint();
+            
+        } 
+        
+        // read whether is_closed is updated
+        if (digit = 10) {
+                
+            // read the keypad for group id and put it in input_group_id
+            char input_group_id[id_size];
+            read_keypad(input_group_id, id_size);
+            
+            if (atoi(input_group_id) - atoi(group_id) == 0) {
+                is_closed = 1;
+            } else {
+                uLCD.printf("Group ID does not match!\nUnable to close session.\n");
             }
         }
-        finger.Enroll(EnrollID,progress);
     }
+    close_session();
+}
 
-    finger.CmosLed(1);
-    while(1) {
-        debug.printf("Press finger for Identify\n");
-        finger.WaitPress(1);
-        if(finger.Capture(1) != 0)
-            continue;
-        ID = finger.Identify();
-        debug.printf("ID = %d\n",ID); 
-        debug.printf("Remove finger\n");
-        finger.WaitPress(0);
+/*
+ * Compare whether the currently scanned fingerprint is contained
+ * in the existing database. Set the this_session_id data structure
+ * to 1 if yes to notify that the person has signed in for this
+ * session.
+ *
+ * @param none
+ * @return none
+ */
+void read_from_fingerprint_scanner() {
+    int FID = -1;
+    
+    //Talk to the fingerprint and get an ID back 
+    uLCD.printf("Press finger for Identify\n");
+    finger.WaitPress(1);
+    if(finger.Capture(1) != 0)
+        continue;
+    FID = finger.Identify();
+    uLCD.printf("ID = %d\n",ID); 
+    uLCD.printf("Remove finger\n");
+    finger.WaitPress(0);
+    
+    if (FID >= 0 & FID < group_size) {
+        this_session_id[FID] = 1;
+        
+        uLCD.printf("Welcome, %s!", STUDENT_NAME);
+        // make this
+    } else {
+        uLCD.printf("Sorry, ID failed!");
     }
 }
+/*
+ * Add new fingerprint into the existing class session
+ *
+ * @param none
+ * @return none
+ */
+void add_new_fingerprint() {
+    
+    while(finger.CheckEnrolled(EnrollID)==0) {
+        EnrollID++;
+    }
+    uLCD.printf("Insert student ID :\n");
+    temp = "903091568";
+    debug.printf("%s\n", temp);
+    EnrollID = sid_file_contains(temp);
+    finger.Enroll(EnrollID, progress);
+
+}
+
+
+/*
+ * Close session is called when a group database
+ * has been uploaded to the fingerprint scanner
+ * 
+ */
+void close_session() {
+    download_to_memory();
+    send_file_via_wifi();
+    // TODO: put changed data in mbed to the micro sd and other housekeeping stuff
+    // Reset the id to be none
+    group_id = 0;
+}
+
+/* Reads from the keypad id_size digits of input
+ * until the user presses the enter button.
+ * If the user presses the cancel button, discard the
+ * digits read and return.
+ * @param char* readable the pointer to put the input read from user
+ * @return int 0 if no change made to the parameter, 1 otherwise
+ */
+int read_keypad(char* readable, int how_many) {
+    
+    char from_user[how_many];
+
+    int index = 0;
+    while (1) {
+        if (index < how_many) {
+            int keypad = get_digit_keypad();
+            
+            if (keypad = 10) {
+                return 0;
+            } else if (keypad = 11) {
+                from_user = readable;
+                return 1;
+            } else {
+                from_user[index] = keypad;
+            }
+            
+            uLCD.printf("%d\n",from_user[index]);
+            uLCD.printf(" * \n");
+        } else if (index == how_many) {
+            index--;        
+        }
+        index++;
+    }
+}
+
+int get_digit_keypad() {
+    int key_code=0;
+    int i=0;
+    int value=keypad.read(0x00);
+    value +=keypad.read(0x01)<<8;
+    
+    i=0;
+    // puts key number out to LEDs for demo
+    for (i=0; i<12; i++) {
+        if (((value>>i)&0x01)==1) key_code=i+1;
+    }
+    
+    if 
+    
+    uLCD.printf("Key pressed is: %d",key_code); // TODO: DEBUG ONLY
+    return key_code;  
+}
+
+void upload_to_scanner() 
+{
+     if(finger.DeleteAll == 0)
+     {
+        debug.printf("All fingerprints deleted");
+     }
+     EnrollID = 1;
+//     group_id = "4180";
+     directory = "/sd/" + group_id ;
+     char ID[3];
+     sprintf(ID, "%d\n",EnrollID);
+     fileName = directory + "/" + ID + ".dat";
+     debug.printf("Attempt to find file %s", fileName);
+     const char* conv_file = fileName.c_str();
+     FILE *templateFile = fopen(conv_file, "r");
+     while(templateFile != NULL)
+     {
+        if(finger.SetTemplate(templateFile, EnrollID, 0 ) == 0)
+        {
+            EnrollID++;  
+            fclose(templateFile); 
+        }
+        char ID[3];
+        sprintf(ID, "%d\n",EnrollID);
+        fileName = directory + "/" + ID + ".dat";
+        const char* conv_file = fileName.c_str();
+        FILE *templateFile = fopen(conv_file, "r");
+     }
+}
+
+void download_to_memory() 
+{
+    EnrollID = 1;
+    directory = "/sd/" + group_id ;
+    const char* conv_dir = directory.c_str();
+    debug.printf("%s", directory);
+    mkdir(conv_dir, 0777);
+    while(finger.GetTemplate(EnrollID)==0)
+    {
+        finger.RecvData(data, 498);
+        char ID[3];
+        sprintf(ID, "%d\n",EnrollID);
+        fileName = directory + "/" + ID + ".dat";
+        debug.printf("Attempt to write to file %s", fileName);
+        const char* conv_file = fileName.c_str();
+        FILE *templateFile = fopen(conv_file, "w");
+        if(templateFile == NULL) {
+                debug.printf("Could not open file for write\n");
+            }
+        fprintf(templateFile, data, EnrollID);
+        fclose(templateFile); 
+        EnrollID++;
+    }
+}
+
+void send_file_via_wifi() {
+    // TODO: Yo
+    // make file
+
+    for (int i = 0; i < group_size; i++) {
+        if (this_session_id[i]) {
+            // append the data to the file to be exported
+        }
+    }
+    // send data
+}
+void getNewLine(File *fp, char *buf, size_t bufsize)
+{
+    char c;
+    size_t receivedChars = 0;
+    for(;;)
+    {
+        c = fgetc(fp);
+        if (c == '\r' || c == '\n')
+            break;
+        buf.putc(c);
+    }
+    return buf;
+}
diff -r 8b9ef3211cd0 -r a1ba925cf903 mpr121.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mpr121.h	Tue Dec 01 19:10:10 2015 +0000
@@ -0,0 +1,58 @@
+/*
+    MPR121.h
+    April 8, 2010
+    by: Jim Lindblom
+*/
+
+// MPR121 Register Defines
+#define MHD_R   0x2B
+#define NHD_R   0x2C
+#define NCL_R   0x2D
+#define FDL_R   0x2E
+#define MHD_F   0x2F
+#define NHD_F   0x30
+#define NCL_F   0x31
+#define FDL_F   0x32
+#define ELE0_T  0x41
+#define ELE0_R  0x42
+#define ELE1_T  0x43
+#define ELE1_R  0x44
+#define ELE2_T  0x45
+#define ELE2_R  0x46
+#define ELE3_T  0x47
+#define ELE3_R  0x48
+#define ELE4_T  0x49
+#define ELE4_R  0x4A
+#define ELE5_T  0x4B
+#define ELE5_R  0x4C
+#define ELE6_T  0x4D
+#define ELE6_R  0x4E
+#define ELE7_T  0x4F
+#define ELE7_R  0x50
+#define ELE8_T  0x51
+#define ELE8_R  0x52
+#define ELE9_T  0x53
+#define ELE9_R  0x54
+#define ELE10_T 0x55
+#define ELE10_R 0x56
+#define ELE11_T 0x57
+#define ELE11_R 0x58
+#define FIL_CFG 0x5D
+#define ELE_CFG 0x5E
+#define GPIO_CTRL0  0x73
+#define GPIO_CTRL1  0x74
+#define GPIO_DATA   0x75
+#define GPIO_DIR    0x76
+#define GPIO_EN     0x77
+#define GPIO_SET    0x78
+#define GPIO_CLEAR  0x79
+#define GPIO_TOGGLE 0x7A
+#define ATO_CFG0    0x7B
+#define ATO_CFGU    0x7D
+#define ATO_CFGL    0x7E
+#define ATO_CFGT    0x7F
+
+
+// Global Constants
+#define TOU_THRESH  0x0F
+#define REL_THRESH  0x0A
\ No newline at end of file
diff -r 8b9ef3211cd0 -r a1ba925cf903 types.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/types.h	Tue Dec 01 19:10:10 2015 +0000
@@ -0,0 +1,65 @@
+//useful things to include in code
+
+#ifndef TYPES_H
+#define TYPES_H
+
+#ifndef WIN32
+    // true/false defines
+    #define FALSE   0
+    #define TRUE    -1
+#endif
+
+// datatype definitions macros
+typedef unsigned char  u08;
+typedef   signed char  s08;
+typedef unsigned short u16;
+typedef   signed short s16;
+typedef unsigned long  u32;
+typedef   signed long  s32;
+typedef unsigned long long u64;
+typedef   signed long long s64;
+
+/* use inttypes.h instead
+// C99 standard integer type definitions
+typedef unsigned char   uint8_t;
+typedef   signed char   int8_t;
+typedef unsigned short  uint16_t;
+typedef   signed short  int16_t;
+typedef unsigned long   uint32_t;
+typedef   signed long   int32_t;
+typedef unsigned long   uint64_t;
+typedef   signed long   int64_t;
+*/
+// maximum value that can be held
+// by unsigned data types (8,16,32bits)
+#define MAX_U08 255
+#define MAX_U16 65535
+#define MAX_U32 4294967295
+
+// maximum values that can be held
+// by signed data types (8,16,32bits)
+#define MIN_S08 -128
+#define MAX_S08 127
+#define MIN_S16 -32768
+#define MAX_S16 32767
+#define MIN_S32 -2147483648
+#define MAX_S32 2147483647
+
+#ifndef WIN32
+    // more type redefinitions
+    typedef unsigned char   BOOL;
+    typedef unsigned char   BYTE;
+    typedef unsigned int    WORD;
+    typedef unsigned long   DWORD;
+
+    typedef unsigned char   UCHAR;
+    typedef unsigned int    UINT;
+    typedef unsigned short  USHORT;
+    typedef unsigned long   ULONG;
+
+    typedef char            CHAR;
+    typedef int             INT;
+    typedef long            LONG;
+#endif
+
+#endif