Project tutorial
Tap To Pay AFC

Tap To Pay AFC

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

  • 1,330 views
  • 0 comments
  • 2 respects

Components and supplies

Apps and online services

About this project

Introduction

Touchless Do More, That's how we start. In amidst of this COVID-19 Pandemic, It asserts the importance of avoiding human to human contact as much as possible to break the chain of this contagious spread. So the problem that we choose to solve is the fare collection. Perhaps it comes down to achieve two aims at once. Meaning that our India really needs some robust system for collecting fare in the public transport sector also during this time of contagious spread, we are in an indispensable need to avoid human to human contact. Hmm. Best of Both Worlds!Even though it was being implemented in all developed countries, It was still not feasible to implement at India on a very large scale. That's where our system comes into play, A Low-cost Fully Automatic Fare Collection System for India's Public Transport Services.

Overview Of The Project

Now, we are going to explain how we made TapToPay Automatic fare collection system using Arduino MKR WiFi 1010 board from Arduino, Mifare MFRC522 RFID Reader/Writer, Google firebase and Google sheets. The MKR board and card reader uses SPI for communication. We have used tags as a bus stop identifier and cards for individual users.

Brief Introduction about RFID Technology

RFID or Radio Frequency Identification system consists of two main components, a tag and a reader. A reader consists of a radio-frequency module and an antenna which generates a high-frequency electromagnetic field. on the other hand, the tag is usually a passive device. It contains a microchip that stores and processes information.

Let's Make It

Step 1: Detect stop card and user card

When the RFID is placed near to the RFID reader, it will be checked with the database to identify whether it is a Stop card or Passenger card. If it is a stop card, then update the current stop in the system.

Step2: Update Passenger List

If the read card ID is not present in the bus stop database then it is a Passenger card. A separate list is maintained for the boarded passengers. Once the passenger card is read, the ID will be searched in the passenger list. If the ID is not present in the list add the passenger ID and the current stop as a boarding point of the passenger.

The passenger list is maintained in a linked list.

int update_pass_list(byte *rfTag, int point, int amount)
{
passenger_t *pass = NULL;

if (!head) {
/* Passenger on boarding case */
if (amount < min_req_amt(point)) {
Serial.print("Problem");
return -1;
}
pass = (passenger_t *) malloc(sizeof(passenger_t));
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;
} else {
pass = search_tag(rfTag);
if (pass) {
/* Passenger getting off case */
/* Deduct money and write */
byte data[MAX_DATA_SIZE] = { };
byte* out = 0;

int_to_byte(Cal_fare(amount, (point - pass->startPoint)), data);

updated_balance = byte_to_int(data);

/* Deleting entry from list */
delete_entry(rfTag);
} else {
/* Passenger on boarding case */
if (amount < min_req_amt(point)) {
/* If the passenger card has low amount then reject it */
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;
memcpy(pass->rfTag, rfTag, RFTAG_MAX_LEN);

pass->startPoint = point;
pass->next = NULL;
tail = tail->next;
}
}
}

Step 3: Calculate fare and update balance

When a passenger is getting off from the bus, the card should be tapped. Our system will read the card and search the ID in the passenger list and will fetch the passengers boarding point from the list. With the current stop and the passenger's boarding stop, the fare will be calculated. The calculated fare will be deducted from the available balance in that passenger's card and the remaining balance will be updated to that card.

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

void int_to_byte(int data, byte buffer[MAX_DATA_SIZE])
{
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 {
/* update dataBlock with new balance amount */
for (i = 0; i < x; i++) {
buffer[i] = 0xff;
}
buffer[x] = y;
}
}

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;
}

Step 4: Delete Entry

After updating the remaining amount to the passenger card that passenger ID will be searched in the list and the entry will be removed from the list. The list maintains only the on-boarded passengers in the 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);
}

Step 5: Send UID to Firebase and update Google sheet

The passenger details will be updated in the cloud database. It will help to track the passenger's route in case he is tested with COVID-19 positive.

The Passenger details are Passenger ID, On-boarding stop with time, Off stop with time.

Demo Video

Software Flow:

Howthis system willslow down Covid-19spread in India?

In India, like in so many other parts of the world, the coronavirus has struct a massive blow to transport. Enhancing the safety of bus transport will be the most pressing issue. In Indian bus transport, cash is being used by the passengers to pay their fare through which there is a possibility of virus transmission. India's move away from cash has been aided by the government. Despite the push, India remains a cash-intensive economy. Some state governments also begin e-ticketing system for its buses to minimise contact. Our Arduino based TapToPay system will play a significant role in the Indian Transport system if implemented.

The video below gives you a quick overview of the project and can be used as a starting point to understand the project.

Enhancements

  • As a prototype, we used a single RFID reader/writer. In a real-time deployment it can be used two or more RFID reader/writer. One for Stop tags and One for passenger cards.
  • A separate RFID reader/writer can be used for recording data of boarding passenger and leaving passenger.
  • A separate server and new database technology can be used to store the passenger travelling details in the cloud.

Code

TTPAFCArduino
#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();
}

Schematics

Schematic
Ttpafc schematic 3 dkjjfgfi4h

Comments

Similar projects you might like

IoT Pressure Sensor: MKR GSM + Arduino Cloud + Google Sheets

Project tutorial by wahltharvey

  • 16,692 views
  • 9 comments
  • 48 respects

Send MKR1000 Data to Google Sheets

Project tutorial by Stephen Borsay

  • 25,431 views
  • 36 comments
  • 57 respects

Arduino-Based Automatic Water Tap Using IR Sensor

Project tutorial by Team Creatjet3D R&D Team

  • 22,246 views
  • 8 comments
  • 27 respects

COVID - 19 Automatic Water Tap

Project tutorial by Akshay Joseph

  • 12,472 views
  • 1 comment
  • 25 respects

Smart Hand Sanitizer

by Team SMD Innovation

  • 11,669 views
  • 12 comments
  • 12 respects

Traffic light with display

Project tutorial by Adhyoksh Jyoti

  • 5,841 views
  • 2 comments
  • 3 respects
Add projectSign up / Login