Thiyagarajan PAasaialangaram SPraveen Kumar RajendranSharathCNRaravind sivakumar
Published

Tap To Pay AFC

A Low-cost Fully Automatic Fare Collection System for India's Public Transport Services

IntermediateFull instructions providedOver 1 day4,778
Tap To Pay AFC

Things used in this project

Hardware components

Arduino MKR WiFi 1010
Arduino MKR WiFi 1010
×1
Breadboard (generic)
Breadboard (generic)
×1
5 mm LED: Red
5 mm LED: Red
×1
5 mm LED: Green
5 mm LED: Green
×1

Software apps and online services

Arduino IDE
Arduino IDE
Arduino IoT Cloud
Arduino IoT Cloud
Google Sheets
Google Sheets
Firebase
Google Firebase

Story

Read more

Schematics

Schematic

Code

TTPAFC

Arduino
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <SPI.h>
#include <MFRC522.h>

#define RFTAG_MAX_LEN       16 // Considering 15 bytes are enough to hold RF TAG
#define MAX_DATA_SIZE       16
#define MAX_AMT_SIZE        MAX_DATA_SIZE
#define FARE_PER_STOP       10
#define LAST_STOP           50
#define MIN_AMT_REQ         FARE_PER_STOP * LAST_STOP
#define MAX_AMT_PER_BYTE    255
#define INIT_AMOUNT         {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}

#define RST_PIN         9           
#define SS_PIN          10          
#define NOT_USED 0

typedef struct passenger {
    char rfTag[RFTAG_MAX_LEN];
    int startPoint;
    struct passenger *next;
} passenger_t;

struct rf_details {
    byte rfTag[5];
    byte stop_no[1];
};

const struct rf_details stop_rf[] = {
    {{0XE9,0XC6,0XB9,0XB1},{1}},
    {{0XA9,0X65,0X21,0XC9},{2}},
    {{0X59,0XB5,0XBA,0XB1},{3}},
    {{0XF9,0XFF,0XBF,0XB1},{4}},
    {{0X49,0X73,0XCB,0XB0},{5}}
};

MFRC522 mfrc522(SS_PIN, RST_PIN);   // Create MFRC522 instance.

MFRC522::MIFARE_Key key;

/* Global Variable Part */ 
void print_balance(byte, byte);
unsigned char data_array[16] = {0};
unsigned int curr_output = 0;
unsigned int prev_output = 0;
unsigned int updated_balance = 0;

/* Global variable to maintain the stop */
int cur_stop = 0;
static passenger_t *head = NULL;
static passenger_t *tail = NULL;
volatile byte bstop_card_ret = '\0';
byte b_busstop = '\0';


// In this sample we use the second sector,
// that is: sector #1, covering block #4 up to and including block #7
byte sector         = 1;
byte blockAddr      = 4;
byte dataBlock[]    = {
    0xff, 0xff, 0xff, 0xff, //  1,  2,   3,  4,
    0xff, 0xff, 0xff, 0xff, //  5,  6,   7,  8,
    0xff, 0xff, 0xff, 0xff, //  9, 10, 255, 11,
    0xff, 0xff, 0xff, 0xff  // 12, 13, 14, 15
};

byte trailerBlock   = 7;
MFRC522::StatusCode status;
byte buffer[18];
byte size = sizeof(buffer);

/*
 * Function to calculate Fare
 */

int Cal_fare(int amt, int num_stop)
{
    return (amt - (num_stop * FARE_PER_STOP));
}

/*
 * Function to calculate minimum amount required to travel
 */

int min_req_amt(int cur_stop)
{
    return (FARE_PER_STOP * (LAST_STOP - cur_stop));
}

/*
 * Function to convert integer to byte
 * @param1  : data, integer that needs to be converted
 * @param2  : buffer, converted data will be stored in
 *            this buffer.
 */

void int_to_byte(int data, byte buffer[MAX_DATA_SIZE])
{
    Serial.print("Balance :");Serial.print(data);
    Serial.println();
    int x = data / MAX_AMT_PER_BYTE;
    int y = data % MAX_AMT_PER_BYTE;
    int i = 0;

    memset(buffer, 0, MAX_DATA_SIZE);

    if (x == 0) {
        buffer[0] = y;
    } else {
        for (i = 0; i < x; i++) {  //update dataBlock with new balance amount
            buffer[i] = 0xff;
        }
        buffer[x] = y;
    }
}

/*
 * Function to convert byte to integer
 * 
 * returns the converted integer value
 */

int byte_to_int(byte buffer[MAX_AMT_SIZE])
{
    int curr_output = 0;
    for(int i = 0; i < MAX_AMT_SIZE; i++) {
        curr_output = curr_output + (int) buffer[i];
    }
    return curr_output;
}

/*
 * Function to print the list
 */

void display_list()
{
    passenger_t *list = head;

    //fprintf(stdout, "\n%16s|%10s|\n", "RF TAG", "Boarding");
    while (list) {
        //fprintf(stdout, "%16s|%10d|\n", list->rfTag, list->startPoint);
        Serial.print("List");Serial.print(list->rfTag[0],HEX);Serial.print("\t");Serial.print(list->startPoint);
        Serial.println();
        list = list->next;
    }
    //fprintf(stdout, "-------------------------------------------------\n");
}

/*
 * Function to search the rfTag in global list
 */

struct passenger *search_tag(byte *rfTag)
{
    passenger_t *list = head;

    if (!rfTag)
        return NULL;

    while (list) {
        if (!memcmp(list->rfTag, rfTag, RFTAG_MAX_LEN))
            return list;
        list = list->next;
    }

    return NULL;
}

/*
 * Function to delete the entry from global list
 */

void delete_entry(byte *rfTag)
{
    passenger_t *list = head;
    passenger_t *prev = NULL;

    if (!rfTag)
        return;

    /* Updating the head in case of first entry itself the match */
    if (list && !memcmp(list->rfTag, rfTag, RFTAG_MAX_LEN)) {
        head = list->next;
        free(list);
        return;
    }

    /* searching the entry in the list */
    while (list && memcmp(list->rfTag, rfTag, RFTAG_MAX_LEN)) {
        prev = list;
        list = list->next;
    }

    if (!list)
        return;

    prev->next = list->next;

    /* Updating the tail */
    if (list == tail)
        tail = prev;

    free(list);
    Serial.println("Entry Deleted");
}

/*
 * Function to check is the card is stop card
 * returns true if it is a stop card else it
 * returns fause
 */

byte is_stop_card(byte tag[RFTAG_MAX_LEN])
{
    int i = 0;

    for (i = 0; i < 5; i++) {
        if (!memcmp(tag, stop_rf[i].rfTag, RFTAG_MAX_LEN))
        {
            //Serial.print("Stop card detected");
            //Serial.println();
            //Serial.println(stop_rf[i].stop_no[0]);
            //Serial.println();
            return stop_rf[i].stop_no[0];
        }

    }

    return false;
}

/*
 * add the entry to the list in case of new entry
 * Deduct and update the amount in case of old entry
 */

int update_pass_list(byte *rfTag, int point, int amount)
{
#if 0
    byte sector         = 1;
    byte blockAddr      = 4;
    byte trailerBlock   = 7;
    MFRC522::StatusCode status;
    byte buffer[18];
    byte size = sizeof(buffer);
#endif

    /*
       Serial.print("In update_pass_list");
       Serial.println();
       Serial.print("User : ");Serial.print(rfTag[0]);
       Serial.println();
       Serial.print("Stop : ");Serial.print(point);
       Serial.println();
     */
    passenger_t *pass = NULL;

#if 0
    if(!head)
    {
        Serial.print("Working");
        Serial.println();
    }
    else
    {
        Serial.print("Not Working");
        Serial.println();
    }
#endif

    if (!head) {
        if (amount < min_req_amt(point)) {
            Serial.print("Problem");
            return -1;
        }
        //Serial.print("Size of structure");
        //Serial.print(sizeof(passenger_t));
        //Serial.println();
        pass = (passenger_t *) malloc(sizeof(passenger_t));
        //Serial.print("Size of structure");
        //Serial.print(sizeof(pass));
        //Serial.println();
        if (!pass) {
            /* This case will hit only when the system in bad state */
            printf("Failed to allocate Memory\n");
            exit (-EXIT_FAILURE);
        }
        head = tail = pass;
        memcpy(pass->rfTag, rfTag, RFTAG_MAX_LEN);
        pass->startPoint = point;
        pass->next = NULL;
        fprintf(stdout, "Passenger [%s] boarded at stop [%d]\n", rfTag, point);
        Serial.println();
        Serial.print("Passenger boarded at stop ");Serial.print(point);
        Serial.print("Available Balance :");Serial.print(curr_output);Serial.println();
        Serial.println();

        Serial.print("----------------------------------------------");Serial.println();

        //Serial.print("User : ");Serial.print(rfTag[0],HEX);Serial.print(rfTag[1],HEX);Serial.print(rfTag[2],HEX);Serial.print(rfTag[3],HEX);
        //Serial.println();
        //Serial.print("Stop : ");Serial.print(point);
        Serial.println();
    } else {
        //Serial.print("Else");
        pass = search_tag(rfTag);
        if (pass) {
            //Serial.print("Inside if");
            /* Detuct money and write */
            byte data[MAX_DATA_SIZE] = { };
            byte* out = 0;

            int_to_byte(Cal_fare(amount, (point - pass->startPoint)), data);
            // TODO: write to RF card

            updated_balance = byte_to_int(data);
#if 1

            status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
            if (status != MFRC522::STATUS_OK) {
                // Serial.print(F("PCD_Authenticate() failed: "));
                // Serial.println(mfrc522.GetStatusCodeName(status));
                return -1;
            }

            // Show the whole sector as it currently is
            // Serial.println(F("Current data in sector:"));
            // mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector);
            // Serial.println();

            // Read data from the block
            //Serial.print(F("Reading data from block ")); // Serial.print(blockAddr);
            // Serial.println(F(" ..."));
            status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);
            if (status != MFRC522::STATUS_OK) {
                // Serial.print(F("MIFARE_Read() failed: "));
                // Serial.println(mfrc522.GetStatusCodeName(status));
                return -1;
            }

            dump_byte_array(buffer, 16); Serial.println();

            // Authenticate using key B
            // Serial.println(F("Authenticating again using key B..."));
            status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid));
            if (status != MFRC522::STATUS_OK) {
                // Serial.print(F("PCD_Authenticate() failed: "));
                // Serial.println(mfrc522.GetStatusCodeName(status));
                return -1;
            }

            // Write data to the block
            //Serial.print(F("Writing data into block ")); // Serial.print(blockAddr);
            // Serial.println(F(" ..."));
            dump_byte_array(data, 16); Serial.println();
            status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(blockAddr, data, 16);
            if (status != MFRC522::STATUS_OK) {
                Serial.print(F("MIFARE_Write() failed: "));
                Serial.println(mfrc522.GetStatusCodeName(status));
            }
            //Serial.println();

            // Read data from the block (again, should now be what we have written)
            ///Serial.println(F("Process Starts"));
            // Serial.println(F("---------------------------------"));
            //Serial.println();
            //Serial.print(F("Reading data from block ")); // Serial.print(blockAddr);
            // Serial.println(F(" ..."));
            status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);
            if (status != MFRC522::STATUS_OK) {
                Serial.print(F("MIFARE_Read() failed: "));
                Serial.println(mfrc522.GetStatusCodeName(status));
            }
            // Serial.print(F("Data in block ")); Serial.print(blockAddr); Serial.println(F(":"));
            dump_byte_array(buffer, 16); //Serial.println();


#endif

            updated_balance = byte_to_int(data_array);

            fprintf(stdout, "Passenger [%s] off at stop [%d]\n", rfTag, point);Serial.println();
            Serial.print("Passenger off at stop ");Serial.print(point);Serial.println();
            Serial.print("Available Balance :");Serial.print(updated_balance);Serial.println();
            Serial.print("***********Thanks For using our system***********");
            Serial.println();
            //Serial.print(rfTag[0],HEX);Serial.print(rfTag[1],HEX);Serial.print(rfTag[2],HEX);Serial.print(rfTag[3],HEX);
            //Serial.println();
            //Serial.print("Stop : ");Serial.print(point);
            Serial.println();
            /* Deleting entry from list */
            delete_entry(rfTag);
        } else {
            if (amount < min_req_amt(point)) {
                Serial.print("Problem");
                return -1;
            }

            pass = (passenger_t *) malloc(sizeof(passenger_t));
            if (!pass) {Serial.print("Failed to allocate Memory");
                /* This case will hit only when the system in bad state */
                printf("Failed to allocate Memory\n");
                exit (-EXIT_FAILURE);
            }

            tail->next = pass;
            //strlcpy — Copy a C-string into a sized buffer
            //Where to copy the string to - pass->rfTag
            //Where to copy the string from - rfTag
            memcpy(pass->rfTag, rfTag, RFTAG_MAX_LEN);

            pass->startPoint = point;
            pass->next = NULL;
            tail = tail->next; // or tail = pass;
            fprintf(stdout, "Passenger [%s] boarded at stop [%d]\n", rfTag, point);
            Serial.print("User : ");Serial.print(rfTag[0]);
            Serial.println();
            Serial.print("Stop : ");Serial.print(point);
            Serial.println();
        }
    }
}

void setup() {
    Serial.begin(9600); // Initialize serial communications with the PC
    while (!Serial);    // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)
    SPI.begin();        // Init SPI bus
    mfrc522.PCD_Init(); // Init MFRC522 card

    // Prepare the key (used both as key A and as key B)
    // using FFFFFFFFFFFFh which is the default at chip delivery from the factory
    for (byte i = 0; i < 6; i++) {
        key.keyByte[i] = 0xFF;
    }

    dump_byte_array(key.keyByte, MFRC522::MF_KEY_SIZE);

    Serial.println(F("TapToPay AFC System Testing"));
    Serial.println(F("----------------------------"));
    Serial.println();
}

/*
 * Main loop.
 */
void loop() {
    // Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle.
    if ( ! mfrc522.PICC_IsNewCardPresent())
        return;

    // Select one of the cards
    if ( ! mfrc522.PICC_ReadCardSerial())
        return;

    bstop_card_ret = is_stop_card(mfrc522.uid.uidByte);
    //Serial.print(bstop_card_ret);
    //Serial.println();

    // Show some details of the PICC (that is: the tag/card)
    dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size);
    // Serial.println();

    /////////////////////////////////////////////////////////////////////////////////
    // Serial.print(F("Card UID DEC Representation::"));
    // dump_byte_array_dec(mfrc522.uid.uidByte, mfrc522.uid.size);
    ////////////////////////////////////////////////////////////////////////////////

    MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);

    // Check for compatibility
    if (    piccType != MFRC522::PICC_TYPE_MIFARE_MINI
            &&  piccType != MFRC522::PICC_TYPE_MIFARE_1K
            &&  piccType != MFRC522::PICC_TYPE_MIFARE_4K) {
        // Serial.println(F("This sample only works with MIFARE Classic cards."));
        return;
    }

    switch(bstop_card_ret)
    {
        case 1:
            b_busstop = bstop_card_ret;
            Serial.print("1st stop card Tapped");
            Serial.println();
            break;

        case 2:
            b_busstop = bstop_card_ret;
            Serial.print("2nd stop card Tapped");
            Serial.println();break;

        case 3:
            b_busstop = bstop_card_ret;
            Serial.print("3rd stop card Tapped");
            Serial.println();break;

        case 4:
            b_busstop = bstop_card_ret;
            Serial.print("4th stop card Tapped");
            Serial.println();break;

        case 5:
            b_busstop = bstop_card_ret;
            Serial.print("5th stop card Tapped");
            Serial.println();break;

        default:
            //Serial.print("No stop card detected");
            Serial.println();break;

    }

    if(bstop_card_ret==0)
    {
        Serial.print("User Card Tapped");

        // Authenticate using key A
        // Serial.println(F("Authenticating using key A..."));
        status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
        if (status != MFRC522::STATUS_OK) {
            // Serial.print(F("PCD_Authenticate() failed: "));
            // Serial.println(mfrc522.GetStatusCodeName(status));
            return;
        }

        // Show the whole sector as it currently is
        // Serial.println(F("Current data in sector:"));
        // mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector);
        // Serial.println();

        // Read data from the block
        //Serial.print(F("Reading data from block ")); // Serial.print(blockAddr);
        // Serial.println(F(" ..."));
        status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);
        if (status != MFRC522::STATUS_OK) {
            // Serial.print(F("MIFARE_Read() failed: "));
            // Serial.println(mfrc522.GetStatusCodeName(status));
        }

        dump_byte_array(buffer, 16); Serial.println();
        print_balance(mfrc522.uid.uidByte, mfrc522.uid.size);

        print_balance(mfrc522.uid.uidByte, mfrc522.uid.size);
        update_pass_list(mfrc522.uid.uidByte,b_busstop,curr_output);
    }

    display_list();
    //curr_output = 0;
    // Halt PICC
    mfrc522.PICC_HaltA();
    // Stop encryption on PCD
    mfrc522.PCD_StopCrypto1();
}

/**
 * Helper routine to dump a byte array as hex values to Serial.
 */
void dump_byte_array(byte *buffer, byte bufferSize) {

    unsigned long long b = 0;
    unsigned short int k, l = 0;

    for (byte i = 0; i < bufferSize; i++) {
        data_array[i] = buffer[i];    
    }
} 

void dump_byte_array_dec(byte *buffer, byte bufferSize) {

    for (byte j = 0; j < bufferSize; j++) {
        Serial.print(buffer[j] < 0x10 ? " 0" : " ");
        Serial.print(buffer[j], DEC);
    }
    Serial.print("\n");
} 


void print_balance(byte *buffer, byte bufferSize)
{
    unsigned long int person_name = 0;
    curr_output = 0;

    Serial.print("***********Displaying User Information***********");
    Serial.println();
    Serial.print("UID :");
    for (byte i = 0; i < bufferSize; i++) {
        Serial.print(buffer[i] < 0x10 ? " 0" : " ");
        Serial.print(buffer[i], HEX);
    }
#if 1
    for (byte j = 0; j < 16; j++) {
        curr_output += data_array[j];
    }
#endif

    Serial.println();
    //Serial.print("Balance :");
    //Serial.print(curr_output, DEC);
    //Serial.println();
}

Credits

Thiyagarajan P
1 project • 0 followers
Experienced Embedded software engineer. Self-motivated, an avid engineer, plays a vital role in the development of the country.
Contact
Aasaialangaram S
6 projects • 16 followers
I am an Electronics Engineer & Passionate about electronics and Artificial Intelligence . Working as Embedded software developer.
Contact
Praveen Kumar Rajendran
1 project • 1 follower
Contact
SharathCNR
0 projects • 1 follower
Contact
aravind sivakumar
1 project • 0 followers
Contact

Comments

Please log in or sign up to comment.