Being curios about how data can be stored and transmitted via radio frequencies, I chose as my final project within faculty to design a ticketing system that calculates the amount owed only at the end of the trip, depending on the distance traveled.
This was my first interaction with an Arduino board and for this reason I believe that the project can be done by anyone who want to. However, it is very important that we understand how this technology works and how the source code is thought.
So let's stop wasting time and let's start the project!
What is RFID technology?Radio-frequency identification uses electromagnetic fields to automatically identify and track tags attached to objects.
Such a system always consists of three components:
- A tag on which data is stored;
- A reader that can have a read or read/write function;
- A controller that has the role of querying a database.
How is the memory of an RFID tag organized?
It's very important to notice what's on an RFID tag, so I've scanned a few tags of my own to show how data is organized. A typical RFID tag has a memory of 1KB structured in 16 sectors and each sector consists of 4 blocks that perform special functions or not.
As can be seen, block 0 of sector 0 is reserved for storing data provided by he manufacturer. In most cases it stores a unique identifier of 4 bytes. The rest sectors consist of 3 data blocks, which can be used to store user data. The fourth block of each sector is know as 'trailer block' and contains the keys necessary to access and the access bits that give indications about the procedures we can perform, for example:
- Read;
- Write;
- Increment;
- Decrement;
- Transfer;
- Restore.
I believe that this information is enough to understand how our system works.
Explaining the source code:In this section I will explain the main functions of the program. In terms of boards and libraries we will use:
Board manager:
- Arduino SAMD Boards.
Required Libraries:
- WiFiNINA;
- FireBase Arduino based on WifiNINA;
- MFRC522.
Steps to follow:
1. To begin with, it is important to read the UID of the RFID tags that will fulfill the role of the stations. For this we will use the program named "DumpInfo" found in the MFRC522 library. I will insert an image to understand where you can find the necessary UID.
2. We need to write an amount of money on the passengers' cards. For that we will use the program named "ReadAndWrite". This code can also be found in the MFRC522 library.
3. Now you need to set up a google firebase and we will connect it with our code by database secret key and URL.
4. At this point you have all the necessary information to configure the code. I will explain most important parts from it:
The function that checks the type of the read tag:
byte is_stop_card(byte tag[RFTAG_MAX_LEN]) {
int i = 0;
for (i = 0; i < 4; i++) {
if (!memcmp(tag, stop_rf[i].rfTag, RFTAG_MAX_LEN)) { return stop_rf[i].stop_no[0]; }
}
return false;
}
As you can see, the program checks if the UID of the read tag is found in the structure configured at the beginning of the code. If it's not found, then it's a passenger tag.
The structure it's the following one and it contains the UIDs:
const struct rf_details stop_rf[] = {
{{0X3C,0X52,0X0E,0X30},{1}}, // Station 1
{{0XB3,0X26,0X42,0X1C},{2}}, // Station 2
{{0X0C,0X2A,0XE9,0X2F},{3}}, // Station 3
{{0XCC,0XA7,0X0D,0X30},{4}} // Station 4
};
Insert and update passenger list:
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
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("Passenger does not have the required amount in the account!");
Serial.println();
return -1;
}
pass = (passenger_t *) malloc(sizeof(passenger_t));
if (!pass) {
printf("Error\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 station [%d]\n", rfTag, point);
Serial.println();
Serial.print("Passenger boarded at station ");
Serial.print(point);
Serial.println();
Serial.print("Balance: ");
Serial.print(curr_output);
Serial.println();
Serial.println();
Serial.print("------------------------------------------");
} else {
pass = search_tag(rfTag);
if (pass) {
byte data[MAX_DATA_SIZE] = { };
byte* out = 0;
int_to_byte(Cal_fare(amount, (point - pass->startPoint)), data);
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) {
return -1;
}
status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);
if (status != MFRC522::STATUS_OK) {
return -1;
}
dump_byte_array(buffer, 16);
status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
return -1;
}
dump_byte_array(data, 16);
status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(blockAddr, data, 16);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Authenticatation Failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
}
status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Authenticatation Failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
}
dump_byte_array(buffer, 16);
#endif
updated_balance = byte_to_int(data_array);
fprintf(stdout, "Passenger [%s] got off at station [%d]\n", rfTag, point);
Serial.println();
Serial.print("Passenger got off at station ");
Serial.print(point);
Serial.println();
Serial.print("Updated Balance: ");
Serial.print(updated_balance);
Serial.println();
Serial.println();
Serial.print("Thank you for using our system!");
Serial.println();
delete_entry(rfTag);
} else {
if (amount < min_req_amt(point)) {
Serial.print("Passenger does not have the required amount in the account!");
Serial.println();
return -1;
}
pass = (passenger_t *) malloc(sizeof(passenger_t));
if (!pass) {
printf("Error\n");
exit (-EXIT_FAILURE);
}
tail->next = pass;
memcpy(pass->rfTag, rfTag, RFTAG_MAX_LEN);
pass->startPoint = point;
pass->next = NULL;
tail = tail->next;
fprintf(stdout, "Passenger [%s] boarded at station [%d]\n", rfTag, point);
Serial.print("Passenger : ");
Serial.print(rfTag[0]);
Serial.println();
Serial.print("Stop : ");
Serial.print(point);
Serial.println();
}
}
}
If the previously discussed function reports that the RFID module has read a passenger card or tag, a new list is created in which the boarded passengers are stored. Upon reading the tag, the UID will be looked up in the list and if it's not present, a new record containing the UID and the boarding station will be inserted.
In the last scenario, the traveler is already in the database and it is understood that he has validated the card to perform the check-out process. In that case the number of stations traveled is calculated and with that information it is possible to withdraw the amount from the card.
The passenger list it's maintained in a simple linked list as follows:
Calculate fare:
This function is simple, it only take two parameters: amount and number of stops. With these the fare can be calculated as it follows:
int Cal_fare(int amt, int num_stop)
Delete entry:
void delete_entry(byte *rfTag)
{
passenger_t *list = head;
passenger_t *prev = NULL;
if (!rfTag)
return;
if (list && !memcmp(list->rfTag, rfTag, RFTAG_MAX_LEN)) {
head = list->next;
free(list);
return;
}
while (list && memcmp(list->rfTag, rfTag, RFTAG_MAX_LEN)) {
prev = list;
list = list->next;
}
if (!list)
return;
prev->next = list->next;
if (list == tail)
tail = prev;
free(list);
Serial.println("Entry Deleted");
}
In the case of deleting a node from the list, there are three situations:
- If the record is in the first position in the list, then all we have to do is change the position of the head of the list;
- As in the previous case, if the record is in the last position, we only need to update the tail of the list;
- A more difficult case is when the record is in the middle. As can be seen in the program, the value before the node to be deleted is retained and its link is changed by two positions.
To understand better the process of deleting a node, I made the following figure:
I think I covered all the necessary information to understand the project. Please leave a comment if you found this helpful.
Comments
Please log in or sign up to comment.