This python project uses a Raspberry Pi 3, an USB GPS Sensor and an RC522 RFID Card Reader in order to log GPS coordinates of specific routes. It's intended usage is for tracking and measuring route distances.
The user uses an RFID Card to start/stop the logging of GPS coordinates, a CSV file is automatically generated that stores the GPS coordinates in a log format.
Below are all the devices and parts that are needed:
- Raspberry Pi 2 or 3 (with MicroSD Card and Power Supply)
- RC522 RFID Reader + Card (and wires for connectivity)
- USB GPS Sensor (NMEA Standard Compatible)
Nice to have:
- LCD Display (with I2C interface)
- Passive Buzzer (5V)
- RGB LED's (or normal ones)
- Power bank (to power the raspberry while moving)
The RFID RC522 has 8 ports and 7 of those we will wire to the Raspberry Pi's GPIO pins as following: SDA (Serial Data Signal) to Pin 24 (GPIO 8), SCK (Serial Clock) to Pin 23 (GPIO 11), MOSI (Master Out Slave In) to Pin 19 (GPIO 10), MISO (Master In Slave Out) to Pin 21 (GPIO 9), IRQ (Interrupt Request) is left unconnected, GND to Pin 6 , RST (Reset-Circuit) to Pin 22 (GPIO 25), and 3.3V (Power In) to Pin 1.
The LCD Display connects to the Raspberry via I2C serial connection as follows: SDA(Serial Data Signal) to Pin 3 (GPIO 2), SCL (Serial Clock) to Pin 5 (GPIO 3), VCC (5V) to Pin 2 and GND to Pin 9.
The USB GPS Sensor will be connected to one of the Raspberry Pi's USB ports. The Raspbian OS will map that USB port to a file in /dev/ folder.
The Passive Buzzer has just 2 wires to be connected: VCC to Pin 13(GPIO 27) and GND to Pin 14.
Before we can run the script we must make sure the Raspberry is setup. First we must make changes to the configuration. By default the Raspberry Pi has the SPI and I2C interfaces disabled.
sudo raspi-config
In the configuration menu we must chose '5 Interfacing Options' and then 'P4 SPI' for SPI and 'P5 I2C' for I2C, press Enter and enable it. After saving the changes you must reboot the Raspberry.
sudo reboot
Once the Raspberry has finished rebooting you can check to make sure the SPI has been enabled by entering the following commands and to check if spi_bcm2835 and i2c_bcm2835 is listed.
lsmod | grep spi
lsmod | grep i2c
The following commands will update our Raspberry Pi and install python3-dev and python3-pip.
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install python3-dev python3-pip
After that we must install the spidev and mfrc522 libraries
sudo pip3 install spidev
sudo pip3 install mfrc522
We must install aditional libraries that are not part of the standard python libraries package. These are pyserial, a library for serial port communications, geopy, a library that will help us calculate the distance between GPS coordinates and smbus which will be used for the LCD display.
sudo pip3 install pyserial
sudo pip3 install geopy
sudo pip3 install smbus
Also, we implemented a REST API used for getting GPS logs from our device with flask. We shall install the flask library that will then install the necessary dependencies.
sudo pip3 install flask
sudo pip3 install flask_restful
At boot time a rc.local is configured to stard a launcher.sh script that will in turn start our python scripts. To setup /etc/rc.local simply add the following line to the end of the file followed by exit 0:
/path/to/bash/script/launcher.sh &
exit 0
Make sure launcher.sh has execute rights and reboot your Raspberry. Now the launcher.sh script should start automatically.
After boot, the first thing that we do is to update the Raspberry Pi's date and time with information from the GPS Sensor. If the GPS device is disconnected the script will wait untill the device is reconnected. After the Raspberry Pi has updated it's date and time (for logging consistency purposes) we start the main tracking script. The script was implemented using threads, there is a worker thread that is listening for RFID Card Readings and signals the Main thread to start logging the GPS data.
A RESTful API developed with flask that provides GPS logs data to GET requests. By default the API works on port 5000. All the responses are in JSON format.
GET Requests implemented so far:
<HOST IP>:<PORT>/routes - returns route data for all the logged routes
<HOST IP>:<PORT>/routes?route_id=<ROUTE_ID> - returns the route data for the specified route ID
<HOST IP>:<PORT>/routes?user_id=<USER_ID> - returns route data for all the routes that were made by a specific user
Parameters:
<HOST IP> - IP of the Raspberry PI (eg. 172.16.3.123)
<PORT> - Port of the flask API (default port is 5000)
<ROUTE_ID> - Number that uniquely identifies the route (starts at 1 for the first logged route)
<USER_ID> - 12 digit number (eg. 780870559455)
This is a bash script that removes all the logs from their locations. This is implemented for testing purposes, to get rid of logs that we don't want and to clean the system.
There are a series of logging mechanisms that are implemented. All the files are stored inside logs folder in the main project folder:
- System Logs: are logs that are generated by:
- tracker.log - logs information about tracker.py script startup, card redings, journey state, debugging information and various errors
- startup_errors.log - logs the stderr generated by tracker.py, time_update.py and api.py scripts
- api.log - logs the activity of the REST API.
- GPS Logs: are logs that are related to the routes GPS data:
- routes.log - logs all the routes in JSON format, each new route on a newline.
- /routes/route_<routeID>_<cardID>.csv - logs all the intermediary GPS coordinates in a csv file.
The global routes log, route.log from gps_logs folder logs the data in JSON format as following:
{"route_id": 1, "timestamp_start": "2020-02-06T13:59:13Z", "lat_start": 44.424587, "lon_start": 26.053704, "user_id": "142189814135", "timestamp_stop": "2020-02-06T14:01:40Z", "lat_stop": 44.422773, "lon_stop": 26.054524, "distance": 242}
{"route_id": 2, "timestamp_start": "2020-02-06T14:02:14Z", "lat_start": 44.422534, "lon_start": 26.05434, "user_id": "780870559455", "timestamp_stop": "2020-02-06T14:05:00Z", "lat_stop": 44.424181, "lon_stop": 26.052981, "distance": 233}
{"route_id": 3, "timestamp_start": "2020-02-06T14:05:17Z", "lat_start": "44.424358", "lon_start": "26.052915", "user_id": "142189814135", "timestamp_stop": "2020-02-06T14:08:53Z", "lat_stop": 44.424475, "lon_stop": 26.053901, "distance": 238}
...
The GPS coordinates are saved in a file that is generated automatically in the gps_logs folder inside the logs folder. It's name is route_<routeID>_<cardID>.csv.
The CSV file contains the timestamp, latitude, longitude and total_distance.
Timestamp Latitude Longitude Total_Distance (m)
2020-02-06T13:59:14Z 44.424582 26.053711 0
2020-02-06T13:59:15Z 44.424571 26.053722 1.5
2020-02-06T13:59:16Z 44.424557 26.053733 3.29
2020-02-06T13:59:17Z 44.424542 26.05374 5.05
2020-02-06T13:59:18Z 44.424525 26.053744 6.97
2020-02-06T13:59:19Z 44.424508 26.053749 8.9
2020-02-06T13:59:20Z 44.424492 26.053753 10.71
2020-02-06T13:59:21Z 44.424475 26.053755 12.61
...
- using a card reader to start/stop the system
- configured logging files for debugging, information and errors
- script that automatically updates system time at startup
- implemented measurement of distance
- system robustness for GPS signal loss
- system robustness for Power Supply loss 53CA
- automatically resuming journey after power failure
- multiple cards management (rejection of foreign cards + only one card per route)
- approximation of distance traveled in case of power failure or GPS signal loss
- global routes log that is used for generating route reports
- LCD Display and Buzzer for system feedback
- Rest API for GPS routes
- added support for 3 languages: english, romanian and hungarian
- implementing a KALMAN filter for better GPS data filtering (requires accelerometer data)
- adding a switch/button to prevent accidental card readings
- adding led indicator for better debugging
- adding a buzzer tone in the case of movement without card readings (as fail-safe)
- database for card users and card id's
- new card registration with master card
- unit tests for the app (with pytest module)