Introduction: GPS System
Project Creator: Carlos Gomez
Having a reliable navigation system is paramount for anyone trying to travel and explore the world.
The most important aspect that allows the navigation system to work is the GPS capability embedded inside of the system. The GPS system allows anyone to keep track of their location and speed in order to display accurate information about the user and give the user an accurate representation of where they are and how far they are from their location.
The Global Positioning System (GPS) is a network of satellites orbiting the Earth at an altitude of about 20,000 km. Anyone with a GPS device can receive the radio signals that the satellites broadcast and is able to utilize them in whichever way needed. Wherever your location on the planet at least four GPS should be available to you at any time. Using a method called 3-D trilateration, a GPS device is able use three satellites in order to determine the device's location at the Earth. Each of the three satellites sends a signal to the device and the device determines it's distance from the satellite. Using each of the three distance calculations the device is now able to pinpoint its location on the Earth and it returns that to the user.
The GPS system we will create will be able to track the user's locations by obtaining the user's coordinates on the Earth and doing some calculations in order to return the user's speed, location, and the distance covered.
Step 1: Getting Started
In order to get this project started, we will first need to gather all the correct materials
1: Raspberry Pi Zero W
2: GPS Receiver
3: 1.8 TFT 128 x 160 LCD SPI Screen
4: ~11 wires
5: 2 buttons
6: 2x 1k and 2x 10k resistors for pull down buttons
7: Bread board
This project will use the Raspberry Pi's GPIO pins and as such we will need to connect everything with a bread board in order to develop our project. It is also assumed the soldering on all the pins is done and finished before moving on and connecting all of our parts.
Step 2: Connect GPS Module to Raspberry Pi
For the use of our GPS system you will need to connect the Tx and Rx pins from the GPS module to GPIO pin 14 and 15 on the Raspberry Pi. The Tx pin of the GPS receiver goes to the Rx pin of the Pi and the Rx pin of the GPS receiver goes to the Tx pin of the Raspberry pi.
The GPS receiver shown in the images require 3.3V to be used and you can connect the 3.3V pins to the correct voltage, while connecting the Ground pin to ground.
Step 3: Recieve Data From GPS Receiver Module
In order to receive data from the GPS receiver to the Raspberry Pi we need to allow the correct sockets to read from the UART ports. Reading the raw data would require us to create our own parsing library, but in this scenario we can take advantage of a GPS daemon that runs in the background to help use parse the data and transmit it to the Raspberry Pi
In order to accomplish this we can open up a terminal on the Raspberry Pi and execute the code:
sudo apt-get update
sudo apt-get install gpsd gpsd-clients python-gps
This should take care of the download for us.
Once it is completed, we need to disable the gpsd system service by running the following commands:
sudo systemctl stop gpsd.socket
sudo systemctl disable gpsd.socket
If you ever want to enable the default gpsd system service, you can run the following commands to restore it:
sudo systemctl enable gpsd.socket
sudo systemctl start gpsd.socket
Now we need to start the gpsd daemon and point it to the UART ports by entering
sudo gpsd /dev/ttyAMA0 -F /var/run/gpsd.sock
We can now run the command below and see all the data floating in!
cgps -s
Step 4: Connect Display to Raspberry Pi
Once we have our GPS receiver up and working with the Raspberry Pi, we can then connect the display to the Raspberry Pi.We will use 5 wires to connect our LCD display to the Raspberry Pi and another 4 pins to connect the main power and LED on the screen.
I have included a photo of the TFT screen I am using, but this should work with screens of similar size and build.
Connect LED- and GND to ground and connect LED+ and VCC to 3.3V.
Connect the RESET pin on the screen to pin 25 on the Pi board.
Connect A0 to pin 24 on the Pi board.
Connect the SDA pin to the MOSI pin on the Pi board.
Connect the SCK pin on the LCD screen to the Pi board.
Connect the CS pin to pin 8 on the Pi board.
Step 5: Setup Display to Work With Raspberry Pi
To setup the display we need to use the ST7735 library found in this repo:
Once we have this display library installed onto our Raspberry Pi system, we can now proceed to setting up an example file to confirm our previous wiring is working correctly.
Create a file titled example.py and insert the following text in there along with a sample image of your choosing in the same folder
<p>import ST7735 as TFT<br>import Adafruit_GPIO as GPIO import Adafruit_GPIO.SPI as SPI</p><p>WIDTH = 128 HEIGHT = 160 SPEED_HZ = 4000000</p><p># Raspberry Pi configuration.</p><p># These are the pins needed to connect the LCD to the Raspberry Pi DC = 24 RST = 25 SPI_PORT = 0 SPI_DEVICE = 0</p><p># Create TFT LCD display class. disp = TFT.ST7735( DC, rst=RST, spi=SPI.SpiDev( SPI_PORT, SPI_DEVICE, max_speed_hz=SPEED_HZ))</p><p># Initialize display. disp.begin() disp.reset()</p><p># Load an image. newData = 0x42 disp.command(newData) print('Loading image...') image = Image.open('cat.jpg')</p><p># Resize the image and rotate it so matches the display. image = image.rotate(270).resize((WIDTH, HEIGHT))</p><p># Will print to the terminal that our program is drawing our Image on the screen print('Drawing image')</p><p># This function will display our image on the screen disp.display(image)</p>
This file will setup the Raspberry Pi configuration for the LCD screen and the library will convert our image in the folder and display it on the screen.
Step 6: Setup State Machines to Display GPS Information on Display
We will use 5 different state machines, while implementing our task diagram to setup our gps system.
Display Change state machine:
This state machine will control which to display depending on our button input. It does this by changing a variable that allows python to take advantage of duck typing and calling the correct function to display depending on the called function
Speed state machine:
This state machine will execute the current speed depending on the individuals location. This will execute every clock cycle for the GPS system
Output state machine:
This state machine will determine the output based on the variable that the display change state machine determines to be the current display.
Distance state machine
This state machine executes every clock cycle and determines the total distance traveled by the user and once the reset button gets pushed, will reset the current distance traveled.
Location state machine:
This state machine returns the current location of the user, using coordinates that the GPS module returns about the user. This state machine is dependent on the users internet connection.
Step 7: Lets Implement Our GPS System!
Once we have our GPS module sending information to our Raspberry Pi and our LCD screen displaying information on it we can then start to program our GPS system. I will use the previous step's finite state machines in order to code our GPS system
<p>#<br># Main file for Navigation system # # # #</p><p># Libraries for drawing images from PIL import Image from PIL import ImageDraw from PIL import ImageFont</p><p># Library for ST7737 controller import ST7735 as TFT</p><p># Library for GPIO for Raspberry Pi import Adafruit_GPIO as GPIO import Adafruit_GPIO.SPI as SPI</p><p># Library for GPS #import gpsd from gps3 import gps3</p><p># Library for time import time</p><p># Library for finding distance between two points from math import sin, cos, sqrt, atan2, radians</p><p># Import Rpi library to use buttons to switch menus and reset # import RPi.GPIO as bGPIO</p><p># Setup pins for buttons</p><p>bGPIO.setmode(bGPIO.BCM) bGPIO.setup(18,bGPIO.IN,pull_up_down=bGPIO.PUD_DOWN)</p><p>bGPIO.setup(23,bGPIO.IN,pull_up_down=bGPIO.PUD_DOWN)</p><p># import geopy library for Geocoding # # Internet access is necessary for this to work</p><p>from geopy.geocoders import Nominatim</p><p>geolocator = Nominatim()</p><p># Constants for system #################################</p><p>WIDTH = 128 HEIGHT = 160 SPEED_HZ = 4000000</p><p># Raspberry Pi configuration pins DC = 24 # A0 on the TFT screen RST = 25 # Reset pin on TFT screen SPI_PORT = 0 # SPI port on raspberry pi, SPI0 SPI_DEVICE = 0 # Slave select on rapsberry pi, CE0</p><p># Create TFT LCD display object disp = TFT.ST7735(DC, rst=RST,spi=SPI.SpiDev(SPI_PORT,SPI_DEVICE,max_speed_hz=SPEED_HZ))</p><p># Initialize display disp.begin()</p><p># Background will be set to green #disp.clear((0,255,0))</p><p># Clear screen to white and display #disp.clear((255,255,255)) draw = disp.draw() #draw.rectangle((0,10,127,150),outline=(255,0,0),fill=(0,0,255)) #disp.display()</p><p># Speed, Latitude, Longitude placement variables #currentS = "Current Speed: " # Speed string #totalDis = "Total Distance: " # Distance string #currentLoc = "Current Location: " # Location string</p><p># Distance x and y coordinates distX = 10 distY = 20</p><p>pointsList = []</p><p># Speed x and y coordinates speedX = 10 speedY = 20</p><p># Location x and y coordiantes locX = 10 locY = 20</p><p># Converts from m/s to mph conversionVal = 2.24</p><p># Speed update function, returns string</p><p>SpeedVar = 0 def speedFunc(): global SpeedVar SpeedText = data_stream.TPV['speed'] if (SpeedText != "n/a"): SpeedText = float(SpeedText) * conversionVal SpeedVar = round(SpeedText,1) # return (SpeedText)</p><p>def locationFunc(): latLoc = str(latFunc()) lonLoc = str(lonFunc())</p><p> reverseString = latLoc + ", " + lonLoc</p><p> location = geolocator.reverse(reverseString) return (location.address)</p><p># Latitude update function, returns float value def latFunc(): Latitude = data_stream.TPV['lat'] if(Latitude == "n/a"): return 0 else: return float(round(Latitude,4))</p><p># Longitude update function, returns string def lonFunc(): Longitude = data_stream.TPV['lon'] if (Longitude == "n/a"): return 0 else: return float(round(Longitude,4))</p><p># Distance function returns TOTAL distance travelled</p><p>totalDistance = 0</p><p>def distFunc(): global totalDistance newLat = latFunc() newLon = lonFunc() if(newLat == 0 or newLon == 0): totalDistance = totalDistance # return (totalDistance) else: pointsList.append((newLat,newLon)) last = len(pointsList)-1 if(last == 0): return else: totalDistance += coorDistance(pointsList[last-1],pointsList[last]) # return totalDistance</p><p># Resets total distance </p><p>def resDistance(): global totalDistance totalDistance = 0</p><p># Function used to find distance between two coordinates # uses Haversine's formula to find. # Input points are a tuple</p><p>def coorDistance(point1, point2): # Approximate radius of the Earth in kilometers earthRadius = 6373.0</p><p> lat1 = point1[0] lon1 = point1[1]</p><p> lat2 = point2[0] lon2 = point2[1]</p><p> distanceLon = lon2 - lon1 distanceLat = lat2 - lat1</p><p> # Haversine a a = sin(distanceLat/2)**2 + cos(lat1) * cos(lat2) * sin(distanceLon/2)**2</p><p> # Haversine c c = 2 * atan2(sqrt(a),sqrt(1-a))</p><p> # Convert km to Miles distance = (earthRadius * c) * 0.62137</p><p> if(distance <= 0.01): return 0.00 else: return round(distance,3)</p><p># Function to display speed on screen</p><p>def dispSpeed(): global SpeedVar # Place distance on variable on screen draw.text((speedX,speedY),str(SpeedVar),font=ImageFont.truetype("Lato-Medium.ttf",72))</p><p># Function to display distance on screen</p><p>def dispDistance(): draw.text((distX,distY),str(totalDistance),font=ImageFont.truetype("Lato-Medium.ttf",60))</p><p># Function ti display location on screen, requires internet to work</p><p>def dispLocation(): draw.text((locX,locY),locationFunc(),font=ImageFont.truetype("Lato-Medium.ttf",8))</p><p># Using dictionary to mimic switch statements</p><p>dispOptions = { 0 : dispSpeed, 1 : dispDistance, 2 : dispLocation }</p><p># Screen output function</p><p>def output(): # Using global variable for displayIndex global displayIndex # Clearing screen and applying background disp.clear((255,255,255)) draw.rectangle((0,10,127,150),outline=(255,0,0),fill=(255,0,0))</p><p> # Calls function depending on displayIndex value dispOptions[displayIndex]()</p><p> # Will erase if other method works</p><p> # place distance variable on screen #draw.text((distX,distY),str(distFunc()),font=ImageFont.load_default()) # place speed variable on screen #draw.text((speedX,speedY),speedFunc(),font=ImageFont.load_default()) # Display updates to screen disp.display()</p><p>displayButton = 18 # BCM Pin on raspberry pi resetButton = 23 # BCM Pin on raspberry pi</p><p>buttonPress = False</p><p>def checkDisplay(): global buttonPress global displayIndex if(bGPIO.input(displayButton) and not buttonPress): displayIndex += 1 buttonPress = True if(displayIndex == 2): displayIndex = 0 elif(bGPIO.input(displayButton) and buttonPress): print ("Still pressed") else: buttonPress = False </p><p># Setup gps gps_socket=gps3.GPSDSocket() data_stream=gps3.DataStream() gps_socket.connect() gps_socket.watch()</p><p>timerPeriod = .5 # Index value for display displayIndex = 0 try: for new_data in gps_socket: if new_data: data_stream.unpack(new_data) if data_stream.TPV['lat'] != 'n/a': print(data_stream.TPV['speed'], data_stream.TPV['lat'], data_stream.TPV['lon']) distFunc() speedFunc() output() checkDisplay() if(bGPIO.input(resetButton)): resDistance() else: output() checkDisplay() if(bGPIO.input(resetButton)): resDistance() print('GPS not connected yet') time.sleep(.1) time.sleep(.8) except KeyboardInterrupt: gps_socket.close() print('\nTerminated by user ctrl+c')</p>
The code above is just one example on how to code our system and I have embedded a video on how this system works.