Add GPS to Your Raspberry Pi Project with Google Maps

Add GPS to Your Raspberry Pi Project with Google Maps social.jpeg

GPS (Global Positioning System), once a military tool developed by the US Navy for submarine location, has now become a commonly accessible technology, thanks to the proliferation of satellites in orbit and technological advancements. Although the technology has evolved in various forms, most users access GPS location functionality through applications on their web and mobile devices. GPS applications are used across industries, from mapping services like Google and Apple Maps to fleet transportation and delivery applications like Uber and Doordash, proximity chat with Snapchat, and even games such as Pokémon Go.

In this tutorial, we'll guide you step-by-step on how to add GPS capabilities to your Raspberry Pi (RPi) project using a GPS Module Breakout written in Python. This module will act as the GPS receiver, retrieving GPS coordinates. To power the functionality of GPS, we'll use PubNub's Geolocation APIs via the latest version of the PubNub Python SDK. Find the source code for this tutorial in the GitHub repository.

This post contains information that may not be entirely up to date. However, this article still contains valuable insights into setting up and retrieving GPS coordinates using a Raspberry Pi and GPS module all in real time with PubNub. You can check out the tutorial below!

Setting Up Your Raspberry Pi GPS Tracker Environment

To complete this tutorial, you'll need the following hardware:

PubNub Account

To power the GPS functionality on the Raspberry Pi, create a free PubNub account and obtain the publish/subscribe API keys necessary to communicate on PubNub's communication platform. Learn how to create your keys by following this how-to guide. Depending on your specific use case, you may choose to enable additional features like Presence and Message Persistence.

Setting Up Your Raspberry Pi Board

Soldering

To easily connect wires to the module and RPi, you’ll need to first solder the pins on the GPS Breakout module using the soldering iron.

Insert the pins from the GPS module (short side first) into the top of the module (the side with the pin labels) and solder the pins to the board. If you are new to soldering, please watch this video first, as you can severely burn yourself.

The GPS breakout module will look like the following once you've finished soldering.

Wiring

To communicate with the GPS breakout module, you’ll need access to the Raspberry Pi’s UART (Universal Asynchronous Receiver-Transmitter) interface. This interface is optimal for sending and receiving data serially from the GPS breakout module to the RPi, rather than through a USB port. The TX (transmitter) pin of the GPS module sends bits to the RPI's RX (receiver) pin and vice versa.

Wire the GPS breakout module to the RPi as shown. Connect the Raspberry Pi to your computer to act as the power supply and program the computer board.

(Optional): 3G/4G LTE Connectivity

If you want your Raspberry Pi to have GPS capabilities without dependence on WiFi or Bluetooth, you’ll need to use an LTE shield. Follow the setup guide included with the LTE shield to connect the shield to the RPi and download the necessary libraries, packages, and GitHub repositories to run the software on the hardware.

Installing Python

This tutorial requires Python3 (version 3.x) and pip3. pip3 is included by default starting with Python 3.4. Ensure Python is added to PATH if you are on Windows.

Enabling Remote Access

Before proceeding, enable remote access on the RPi. Follow Raspberry Pi's official documentation to do so.

Enabling UART on the Raspberry Pi

Open a terminal/console, and SSH into the RPi. Run the following command to open The Raspberry Pi Software Configuration Tool (raspi-config), which allows you to enable/disable specific features for the RPi.

sudo raspi-config

Follow the steps to enable UART:

  1. Select Interfacing Options.

  2. Select P6 Serial

  3. Do not allow a login shell to be accessible over serial.

  4. Allow the serial port hardware to be enabled.

Then reboot the RPi with the following command.

sudo reboot

Configuring I2C

You will need the CircuitPython Library for this tutorial, as many of the I2C drivers require this library. This is a 2-wire bus protocol that allows one chip to communicate with another.

SSH into the RPi and install the I2C tools utility with the following commands.

sudo apt-get install -y python-smbus
sudo apt-get install -y i2c-tools

For this library to function correctly, you also need to install Kernel Support. Open the RPi software configuration tool.

sudo raspi-config

Install Kernel Support by following the correct menu options.

  1. Select Interfacing Options.

  2. Select I2C.

  3. Enable the ARM I2C interface.

  4. Enable the I2C kernel module to be loaded by default.

Then reboot the RPi.

sudo reboot

Configuring SPI

SPI (Serial Peripheral Interface) is another bus protocol that synchronizes serial data communication between chips and will need to be configured for this tutorial. SSH into your RPi and open the RPi Software Configuration Tool.

sudo raspi-config

Configure SPI by following the steps below.

  1. Select Interfacing Options.

  2. Select SPI.

  3. Enable the SPI interface to be enabled.

Then reboot the RPi.

sudo reboot

Verifying Hardware

To ensure that the previous steps were performed correctly, you will be running a test script to verify that all the necessary hardware protocols are enabled.

SSH into the RPI and create a working directory for your project.

mkdir gps
cd gps

Install the Raspberry Pi GPIO and Adafruit's test libraries using pip.

pip3 install RPI.GPIO
pip3 install adafruit-blinka

Create the test script named blinkatest.py with nano.

nano blinkatest.py

Copy and paste the following Python code into the test Python script and save.

import board
import digitalio
import busio
 
print("Hello blinka!")
 
# Try to great a Digital input
pin = digitalio.DigitalInOut(board.D4)
print("Digital IO ok!")
 
# Try to create an I2C device
i2c = busio.I2C(board.SCL, board.SDA)
print("I2C ok!")
 
# Try to create an SPI device
spi = busio.SPI(board.SCLK, board.MOSI, board.MISO)
print("SPI ok!")
 
print("done!")

Run the test script with the following command.

python3 blinkatest.py

You should receive the following output. If not, go back and repeat the previous steps based on what is failing.

Installing CircuitPython

Finally, install CircuitPython. CircuitPython, based on Python, is designed to make programming microcontrollers easily accessible for beginners. This library bundle, along with the GPS Python library for GPS parsing modules, will serve as the starting points of your codebase.

Download the zip (not the source zip) from the latest release of the repository from Adafruit and unzip the contents in your project directory.

`cd` into the library you just unzipped and then clone the GPS Python library to use as the GPS parsing module library. Install any dependencies with the following command.

sudo pip3 install adafruit-circuitpython-gps

Verifying Hardware and Software

To ensure a smooth setup of both hardware and software, you'll need to run an example script in the circuitpython-gps library that was just cloned. Navigate to your project directory and enter the following commands in the terminal to open the gps_simpletest.py test file.

cd Adafruit_CircuitPython_GPS
cd examples
Nano gps_simpletest.py

In the gps_simpletest.py file, locate and comment out the following lines to ensure the program runs seamlessly with the Raspberry Pi.

RX = board.RX
TX = board.TX
uart = busio.UART(TX, RX, baudrate=9600, timeout=3000)

Next, uncomment the following lines.

#import serial
#uart = serial.Serial("/dev/ttyUSB0", baudrate=9600, timeout=3000)

The second line that was just uncommented needs to be modified to the following.

#uart = serial.Serial("/dev/ttyS0", baudrate=9600, timeout=3000)

You're now geared up to run the code. Ensure that the GPS module has a clear view of the sky and run the code with the following command.

python3 gps_simpletest.py

As the GPS module searches for the GPS satellites in the sky, you will receive the following terminal output.

Once a connection is established with a GPS satellite, you will receive GPS data readings that could look something like this.

Build and Run

You're now ready to start implementing code to add GPS functionality to your Raspberry Pi! Instead of creating an entirely new file, you will make a few alterations to the gps_simpletest.py file that was used for testing earlier, and add PubNub functionality to relay GPS information across the PubNub network.

In the same directory of your project that contains the gps_simpletest.py file, install the latest version of the PubNub Python SDK using the terminal.

pip install 'pubnub>=7.3.2'

In the gps_simpletest.py file, import the PubNub packages.

import pubnub
from pubnub.pnconfiguration import PNConfiguration
from pubnub.pubnub import PubNub
from pubnub.callbacks import SubscribeCallback
from pubnub.enums import PNOperationType, PNStatusCategory

At the start of the file, after importing the packages, configure a PubNub instance with your PubNub Publish and Subscribe Keys obtained earlier. Also, add a publishing callback to listen for publish events. It will be left blank for now.

pnconfig = PNConfiguration()
pnconfig.subscribe_key = "YOUR SUBSCRIBE KEY"
pnconfig.publish_key = "YOUR PUBLISH KEY"
pnconfig.ssl = False
pubnub = PubNub(pnconfig)

def publish\_callback(result, status):
    pass
    # Handle PNPublishResult and PNStatus

The data to be published will be the latitude and longitude coordinates. This data will be published in JSON format and can be published in the following way. Don't forget to replace "CHANNEL" with your own PubNub channel name.

dictionary = {"latitude": gps.latitude, "longitude": gps.longitude}
pubnub.publish().channel("CHANNEL").message(dictionary).pn_async(publish_callback)

Visualizing GPS Data with Google Maps

The final step of this tutorial is to visualize the GPS data in a way that is easily comprehensible. You will create a simple HTML page that will grab GPS data from the PubNub channel and graph the data with a geolocation API. You will be using the latest version of the Google Maps API to graph the data received by your Raspberry Pi. To use the Google Maps API, you need to obtain a Google Maps API Key. Learn how to do so from Google's official documentation.

Create a file in your project directory with an .html extension and copy the following code into the file. Where the script tag is using the Google Maps API, replace the value of the key "YOUR_KEY_HERE" with your Google Maps API key. You also need to add the subscribe key in the PubNub initialization that you obtained earlier.

<!DOCTYPE html>
<html>
  <head>
    <title>Simple Map</title>
    <meta name="viewport" content="initial-scale=1.0">
    <meta charset="utf-8">
    <style>
      /* Always set the map height explicitly to define the size of the div
       * element that contains the map. */
      #map {
        height: 100%;
      }
      /* Optional: Makes the sample page fill the window. */
      html, body {
        height: 100%;
        margin: 0;
        padding: 0;
      }
    </style>
    <script src="https://cdn.pubnub.com/sdk/javascript/pubnub.7.5.0.js"></script>
  </head>
  <body>
    <div id="map"></div>
    <script>
  // the smooth zoom function
  function smoothZoom (map, max, cnt) {
      if (cnt >= max) {
          return;
      }
      else {
          z = google.maps.event.addListener(map, 'zoom_changed', function(event){
              google.maps.event.removeListener(z);
              smoothZoom(map, max, cnt + 1);
          });
          setTimeout(function(){map.setZoom(cnt)}, 80); // 80ms is what I found to work well on my system -- it might not work well on all systems
      }
  } 
    var pubnub = new PubNub({
    subscribeKey: "YOUR SUBSCRIBE KEY",
    ssl: true
  });	
  var longitude = 30.5;
  var latitude = 50.5;
  pubnub.addListener({
      message: function(m) {
          // handle message
          var channelName = m.channel; // The channel for which the message belongs
          var channelGroup = m.subscription; // The channel group or wildcard subscription match (if exists)
          var pubTT = m.timetoken; // Publish timetoken
          var msg = m.message; // The Payload
          longitude = msg.longitude;
          latitude = msg.latitude;
          var publisher = m.publisher; //The Publisher
    var myLatlng = new google.maps.LatLng(latitude, longitude);
    var marker = new google.maps.Marker({
        position: myLatlng,
        title:"PubNub GPS"
    });
    // To add the marker to the map, call setMap();
    map.setCenter(marker.position);
    smoothZoom(map, 14, map.getZoom());
    marker.setMap(map);
      },
      presence: function(p) {
          // handle presence
          var action = p.action; // Can be join, leave, state-change or timeout
          var channelName = p.channel; // The channel for which the message belongs
          var occupancy = p.occupancy; // No. of users connected with the channel
          var state = p.state; // User State
          var channelGroup = p.subscription; //  The channel group or wildcard subscription match (if exists)
          var publishTime = p.timestamp; // Publish timetoken
          var timetoken = p.timetoken;  // Current timetoken
          var uuid = p.uuid; // UUIDs of users who are connected with the channel
      },
      status: function(s) {
          var affectedChannelGroups = s.affectedChannelGroups;
          var affectedChannels = s.affectedChannels;
          var category = s.category;
          var operation = s.operation;
      }
  });
  pubnub.subscribe({
      channels: \['ch1'\],
  });
      var map;
      function initMap() {
        map = new google.maps.Map(document.getElementById('map'), {
          center: {lat: latitude, lng: longitude},
          zoom: 8
        });
      }
    </script>
    <script src="https://maps.googleapis.com/maps/api/js?key=YOUR\_KEY\_HERE&callback=initMap"
    async defer></script>
  </body>
</html>

This HTML file will render the map on the HTML page, as well as make use of the latest version of the PubNub JavaScript SDK to interact with the PubNub network to retrieve the GPS data obtained from the Raspberry Pi that was set up earlier. An event listener is used to listen for any new information and uses the Google Maps API key to graph this information using the Google Maps API. The function smoothZoom is used to add a smooth zooming animation every time you locate a marker. The map is initialized with initial longitude and latitude coordinates that you can adjust to suit your needs.

Save your work and double-click the HTML file to open it in your default web browser. You'll be able to visually see the GPS information retrieved by your Raspberry Pi and GPS module in real time!

What's Next

You now have an application that can visually view the GPS coordinates that your Raspberry Pi and GPS module retrieve from satellites. Using PubNub, this information is updated in real time. You can find the source code of this application in the GitHub repository.

If you'd like to learn more about how to power other IoT applications, look at our IoT resources.

If you have any other questions or concerns, please feel free to reach out to devrel@pubnub.com.