Using GPS and RFID cards to track passenger movement and assign fares in buses

Hi,

I've started a project for my college work and I've been able to make some headway on my own while checking for code and ideas online. However, I feel that I've reached a stage where I could use some feedback on whether I'm heading in the right direction.

The basic idea of the project is to use Arduino, a GPS module, a RFID card reader and RFID cards to calculate the fare for passengers that use public transport. I'll also include a 16x2 LCD screen for visual messages.

A brief background on how public service vehicles (PSVs) operate in my country.

In Kenya, we have PSVs called matatus that operate all over. They can be of different sizes ranging from 6 seater to 50 seater. The most common are the 14 and 33 seater.

The matatus pick up and drop off passengers at different stages along the route but they also frequently drop off people at any place along the road. The pick up points and drop off points are never fixed. On a route 40km long there could be 15 official pick up/drop off points but unofficial can be as high as 40.

The fare for each passenger is determined by the conductor/tout of that vehicle. He/she decides based on time of day, day of the week, from what stage to what stage and any other factor.

The above system has a lot of issues as it is cash based, not predictable and prone to the whims of the matatu touts. I wish to introduce a cashless predictable system

How my system works

I propose to use distance as the measure of how much a passenger should be charged for the trip they make. A lot of the current taxi hailing apps use both distance and time but for the purpose of this project, distance is enough

There would be a base fee which is applied all the time and when the passenger travels a certain distance, then the passenger will be charged a fee per distance.

So it can be of the form of y=mx+c where

  • y is the total fee
  • m is the rate per km
  • x is the distance travelled
  • c is the base fare

The distance needs to be calculated. For this I've decided to use a GPS module. I could have used Google maps but the issue is that our matatus don't always stick to known roads and can use rough roads or gullies to get to their destination. So using maps wouldn't have been easy. Calculating the distance makes more sense.

The distance would be calculated every 1-2 seconds and added to get a cumulative total. So say the matatu moved 15m the first sec, then 25 the next sec and 25 again. So say in a span of 30 min they moved a cumulative total of 45km. The passenger would be charged for that 45km

Where does RFID come in? The system has to find a way of keeping track of each and every passenger since not everyone entered at the same stage. So whenever a passenger enters the vehicle, they swipe their card against the RFID reader and the system records the position where they started from. The passenger at that point is assigned a 0 total and from that point on they will get their distances added up until they leave

When the passenger leaves, then they have to swipe again to show that they have left. The system has to calculate the final total of distance, get the fee for that distance and add the base fee and finally display the fare to be charged. The fare is then deducted from the RFID card.

Arduino brief sketch:

  • Scan for RFID card
  • Save RFID card ID to a table with distance column
  • The distance column starts at 0
  • Every 1 second, calculate the distance, add it to the figure in the distance column for that ID
  • The above step is done for all IDs in the table
  • Every 1 second, the Arduino scans to check if the card ID is still in the system or not
  • It also checks to see if the same ID has been swiped out
  • If the card is swiped out, finalize the total, calculate the fare and display the result
  • Once swiped out and the result displayed, the fare is deducted from the card
  • And finally the ID is removed from the table and the distance reset to 0

So far I've been able to get a code working for calculating the distance every 1.5 seconds (I can change this time interval)

I've also got the code to check for new IDs and store them

I'm stopping at this juncture to see if there's something else I need to do before proceeding. If all this looks good, I'll post the code soon

Thank you

Chintan Gohel

Fascinating project. What are you studying?

You've given that a huge amount of thought and well done on already having the system figure out distance and register passengers with their cards.

Presumably you have some ideas for how passengers would buy cards that are charged with a certain amount of money? (And then top them up when the money runs low.)

From a practical real world implementation point of view, as opposed to college coursework, how would you handle cash customers? (ie, those who may be using a mutatu for the first and only time, or who left their card in their other pants.) And what if there's not enough money in the card when the system tries to subtract the fare?

not_a_noob:
Fascinating project. What are you studying?

You've given that a huge amount of thought and well done on already having the system figure out distance and register passengers with their cards.

Presumably you have some ideas for how passengers would buy cards that are charged with a certain amount of money? (And then top them up when the money runs low.)

From a practical real world implementation point of view, as opposed to college coursework, how would you handle cash customers? (ie, those who may be using a mutatu for the first and only time, or who left their card in their other pants.) And what if there's not enough money in the card when the system tries to subtract the fare?

Thank you for the good words, yes, I've been thinking on this for a while now. I had studied Electrical Engineering - this project is supposed to be my final year project which is long overdue

For the time being, I just want to try the part of the charging per distance. Topping up the card and connecting it with a banking or money system will be a later stage after I've submitted this to the college.

A few years back a similar cashless system was tried which failed after a few months. The issues were that there were too many steps in the payment process and not everyone was using it.

For my college project, I don't need to show it in the real world with matatus, I can just demonstrate the practical nature using a couple of staff busses.

However, if this ever did reach practical real world application, then cash customers would still be accepted though in the long run we would try to phase them out.

If there's not enough money in the card, a negative balance is created which has to be eliminated on the next top up or the card will be declined. The cards would be tied with a unique identification such as national ID or phone number. This would make it hard to have dozens of cards per person.

Chintan_Gohel:
For my college project, I don't need to show it in the real world with matatus, I can just demonstrate the practical nature using a couple of staff busses.

That makes sense.

Chintan_Gohel:
A few years back a similar cashless system was tried which failed after a few months.

Where I live it would fail because a) there's way too much rivalry between taxi associations to prevent any standardisation, and a passenger would likely need a few cards to complete a journey across association "boundaries" and b) in any case, the so-called "tenderpreneurs" would syphon off most of the government funding that such a scheme would surely require.

I’m posting the code for calculating distance after every time period:

#include <SoftwareSerial.h>
#include <TinyGPS.h>
#include <LiquidCrystal.h>
#define RXPIN 19
#define TXPIN 18
#define GPSBAUD 9600
TinyGPS gps;
SoftwareSerial uart_gps(RXPIN, TXPIN);
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
void getgps(TinyGPS &gps);
double dist=0;
double x[10];
double y[10];
double distance=0;
int i=0;
void setup()
{
  Serial.begin(9600); uart_gps.begin(GPSBAUD); 
  Serial.println(""); Serial.println("GPS Shield QuickStart Example Sketch v12");
  Serial.println("       ...waiting for lock...           "); Serial.println("");
}
void loop()
{ 
      if(uart_gps.available())    // While there is data on the RX pin...
      {
          int c = uart_gps.read();    // load the data into a variable...      
          if(gps.encode(c))      // if there is a new valid sentence...
          {
            getgps(gps);         // then grab the data.
          }
      }                 
}
void getgps(TinyGPS &gps) 
{
            float lat, lon; double dlat; double dlon;
            gps.f_get_position(&lat, &lon);
            Serial.print("Lat/Long1: "); Serial.print(lat,8); Serial.print(", "); Serial.println(lon,8);
            x[i]=lat*100000000; y[i]=lon*100000000; 
            if (i==0){
                        dlat = abs(x[0]- x[9]) * 111194.9;
                        dlon = 111194.9*abs(y[0] - y[9])*cos(radians((x[0] + x[9])/2));         
                      }
            else      {
                        dlat = abs(x[i]- x[i-1]) * 111194.9;
                        dlon = 111194.9*abs(y[i] - y[i-1])*cos(radians((x[i] + x[i-1])/2));
                      }
            dist = 0.00000001*sqrt(pow(dlat, 2) + pow(dlon, 2));
            if (dist<5)
                      {
                        distance=distance+0;
                      }
            else 
                      {
                        distance=distance+dist;
                      }
            Serial.println(dist, 8);
            Serial.println(distance, 8); 
            lcd.begin(16,2);
            lcd.setCursor(0, 0); 
            lcd.print(dist,8); 
            lcd.setCursor(0, 1);
            lcd.print(distance,8); 
            if (i==9)
                      { 
                        i=0;
                      }
            else 
                      {
                        i=i+1;
                      }  
            delay(1500); 
}

So what does the code above do and how does it work?

The void setup code starts the GPS module - the modeule model number is GY-NEO6MV2 - I’ll attach the data sheet

I’m using an Arduino mega

When I was first trying out this code, I wasn’t get any readings and I realized that the latidue and longitude signals or values had several decimal places which were being cut off in the formula for calculating distance. So I had to multiply those latitudes and longitudes by 108 to get a value that can then be used to calculate the distance.

To calculate the distance covered between 2 points, you need to know the latitude and longitude for those 2 points. Once you have them, you can then get the difference and use that to calculate distance.

Think of the points as x,y coordinates in a 2d plane. To get the distance between those 2 points, it’s basically high school mathematics - square root of ((dx)2+(dy)2)

However, since this is a real world situation, the latitude and longitude need to be converted to meters - that’s where the formula with the radians comes in

Since I had multiplied the latitude and longitude by 108 I’ve taken care of that by reducing it by the same factor to bring it back to a normal value

During testing, I realized that the GPS module would have some errors when retrieving the coordinates even when sitting in a fixed position. To take care of that, I only take in values that are greater than 5m

NEO-6_DataSheet_(GPS.G6-HW-09005).pdf (866 KB)

The TinyGPS++ library does have its own routine for calculating distance (and direction) between two sets of GPS co-ordinates.

srnet:
The TinyGPS++ library does have its own routine for calculating distance (and direction) between two sets of GPS co-ordinates.

I know this is going to sound bad but I remember trying that 2 years ago or so but I wasn’t getting the results I wanted. So I had to try a different method. When I had tried it back then, the serial monitor was giving me strange results - a lot of question marks, boxes, apostrophes etc.

I’ve just wired my board and GPS and tested the code I had written and it still works.

Update on the distance calculation code.

I have changed the minimum value to record distance as 2m and I've also changed the time interval to be 2s

I did a test today while I was out of the house. We drove about 3.42km while the code showed 3000m.

So there's somewhere the code is calculating less. The GPS module is accurate -if we don't move, it shows the distance moved as 0 and repeats this value as long as we are not moving

What I have noticed is that the time stamp difference between successive values is longer than the time interval I've set to calculate with

I will try another test on Saturday when I get to work and there's less traffic with straight roads.

Any suggestions on increasing accuracy?

Chintan_Gohel:
I know this is going to sound bad but I remember trying that 2 years ago or so but I wasn't getting the results I wanted. So I had to try a different method. When I had tried it back then, the serial monitor was giving me strange results - a lot of question marks, boxes, apostrophes etc.

I've just wired my board and GPS and tested the code I had written and it still works.

I have used it for 3 years or more, it has always worked correctly when writing to serial monitor, several types of display and SD card.

If you were getting 'strange results' on the serial monitor you were doing something wrong.

Hi guys,

I want to say sorry for being away for this long. I had some issues to work out at the college first before I could continue. But now I'm back and I would want to finish this before end of October

I'm going to post my ideas below once I've thought about them in detail

Thank you

Interesting thing here is at the “heart” of the problem you have a great Arduino solution that would be easy to roll out.

When a passenger enters the bus, they / driver presses a button and a ticket would print with a barcode ID of the current GPS coordinates. When said passenger goes to exit the bus, they scan their ticket and the system calculates the total due based on current GPS and your fare calculations, rider still pays in cash. If they can’t produce a ticket then they pay full fare.

Then later I would look into making a “cash free” payment option, heck maybe you could store the boarding GPS coordinates on the RFID card itself so your not trying to maintain a database of riders.

If I were to take the code I have written earlier, the one for calculating the distance every time period, is there a way to output that distance value continuously and feed that value to a different code section? What would this concept be called? So that I can get the right way of writing the code

The idea that I am working with is that the distance has to be fed into the code section that deals with the RFID cards - each person who uses the RFID cards has to be assigned that distance they travelled. Meaning the distance has to come from somewhere

So what would I need to do to export the distance values from the distance calculating section and feed them to the RFID section?

Thank you

Chintan_Gohel:
I’m posting the code for calculating distance after every time period:

#include <SoftwareSerial.h>

#include <TinyGPS.h>
#include <LiquidCrystal.h>
#define RXPIN 19
#define TXPIN 18
#define GPSBAUD 9600
TinyGPS gps;
SoftwareSerial uart_gps(RXPIN, TXPIN);
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
void getgps(TinyGPS &gps);
double dist=0;
double x[10];
double y[10];
double distance=0;
int i=0;
void setup()
{
 Serial.begin(9600); uart_gps.begin(GPSBAUD);
 Serial.println(""); Serial.println(“GPS Shield QuickStart Example Sketch v12”);
 Serial.println("       …waiting for lock…           “); Serial.println(”");
}
void loop()
{
     if(uart_gps.available())    // While there is data on the RX pin…
     {
         int c = uart_gps.read();    // load the data into a variable…      
         if(gps.encode(c))      // if there is a new valid sentence…
         {
           getgps(gps);         // then grab the data.
         }
     }                
}
void getgps(TinyGPS &gps)
{
           float lat, lon; double dlat; double dlon;
           gps.f_get_position(&lat, &lon);
           Serial.print("Lat/Long1: “); Serial.print(lat,8); Serial.print(”, "); Serial.println(lon,8);
           x[i]=lat100000000; y[i]=lon100000000;
           if (i==0){
                       dlat = abs(x[0]- x[9]) * 111194.9;
                       dlon = 111194.9*abs(y[0] - y[9])cos(radians((x[0] + x[9])/2));        
                     }
           else      {
                       dlat = abs(x[i]- x[i-1]) * 111194.9;
                       dlon = 111194.9
abs(y[i] - y[i-1])cos(radians((x[i] + x[i-1])/2));
                     }
           dist = 0.00000001
sqrt(pow(dlat, 2) + pow(dlon, 2));
           if (dist<5)
                     {
                       distance=distance+0;
                     }
           else
                     {
                       distance=distance+dist;
                     }
           Serial.println(dist, 8);
           Serial.println(distance, 8);
           lcd.begin(16,2);
           lcd.setCursor(0, 0);
           lcd.print(dist,8);
           lcd.setCursor(0, 1);
           lcd.print(distance,8);
           if (i==9)
                     {
                       i=0;
                     }
           else
                     {
                       i=i+1;
                     }  
           delay(1500);
}

For the interim solution, yes, this can also be an idea. When such a project would be rolled out, definitely not everyone would be having their contactless cards, some would still be using cash and this would be a good way to help them out while they transition to contactless card system

Slumpert:
Interesting thing here is at the “heart” of the problem you have a great Arduino solution that would be easy to roll out.

When a passenger enters the bus, they / driver presses a button and a ticket would print with a barcode ID of the current GPS coordinates. When said passenger goes to exit the bus, they scan their ticket and the system calculates the total due based on current GPS and your fare calculations, rider still pays in cash. If they can’t produce a ticket then they pay full fare.

About the second part, so if I understand it correctly, the gps coordinate would be stored in the card instead of the arduino recording when the person entered the vehicle. I think here the arduino would still have to record that UiD of the card and perform the calculations in the background until the passenger alights. Just using the starting and ending gps coordinates would result in calculating a straight line between the 2 points and not the actual path.

Then later I would look into making a “cash free” payment option, heck maybe you could store the boarding GPS coordinates on the RFID card itself so your not trying to maintain a database of riders.

Your right, it’s even simpler. All you need to print on the ticket/store on the card is the current mileage.

I think I've worked out how to do what I wanted

My original thinking and problem was how to export the distance from the distance calculating function to the RFID function - the solution was simple and staring at me in the face

I had made the distance variables global so there shouldn't be a problem in working with the distance variables

Now I just need to look at the RFID codes again and condense it down to what I need and add it to the rest of the code

Taking a break this long has seriously put me back several steps