Introduction: Arduino GPS Logger

About: I'm curious and therefore I can do a lot and nothing at the same time

Hi guys,

I'm getting super exited for little projects that allow people to actually understand a lot more of the technology that we have around everyday.

This project is about GPS breakout and SD logging. I learned a lot just building this stuff.

There are a lot of notions that you will gain following this tutorial, and much more following the link I provide to get deeper in the topics.

So, what it that? Simple: Is a GPS tracker that log positions (with altitude too), speed and date/time on a microSD.

What you will need:

- Arduino Nano (I actually used a UNO to build the sketch, but they're just the same!)
- Adafruit ultimate GPS breakout
- MicroSD card breakout
- Soldering tools (everything you'll need for solder)
- Universal Stripboard (I used a 5x7cm)
- Wires

All those component are pretty cheap except for the GPS module. That is about 30-40 dollars and Is the most expensive part. Even a new soldering iron set could costs less.

Exists also an Adafruit shield with GPS and SD card modules together. If you want to use it, keep in mind that is made for Arduino UNO, therefore you'll need a UNO and not a Nano. There is no difference in the sketch though.

Let's go further...

Step 1: Connecting Components

Well, after you got the components, you'll need to connect them. Here you can find the fritzing schematics that is pretty clear. However, heres the pinout too:

MicroSD breakout:

5V -> 5V
GND -> Gnn
CLK -> D13
DO -> D12
DI -> D11
CS -> D4 (If you're using the shield this is built in D10)

GPS breakout:

Vin -> 5V
Gnn -> Gnn
Rx -> D2
Tx -> D3

Little notes about those module: Those two little boys are communicating through different paths with the Arduino. The GPS use a TTL Serial, the same kind we use when we communicate with Arduino via Serial Monitor, that why we have to declare through a library a new serial (Tx and Rx) because GPS wants to use the 9600 by default, and we want to use it either. The GPS module is always and constantly streaming data, if plugged. This is the tricky part to deal with, because if we read a sentence and than print it, we could lose the next one, that is needed too. We have to keep it in mind when coding!

The MicroSD communicates via SPI (Serial Peripheral Interface), another way to communicate with the board. Those kind of module use always CLK on the D13, DO on the D12 and DI on the D11. Sometimes those connections have different name like CLK = SCK or SCLK (Serial Clock), DO = DOUT, SIMO, SDO, SO, MTSR (all those indicate Master Output) and DI = SOMI, SDI, MISO, MRST (Master Input). Finally we have the CS or SS that indicates the pin where we send what we want to write in the MicroSD. If you want to use two different SPI modules, you just have to differentiate this pin in order to use them both.
For example, LCD screen AND a MicroSd like the one we are using. It should work also using two different LCDs connected to different CSs.

Solder these part together in the board and you're all set to upload the sketch!

As you can see in the sketch, I solder some dupont female connectors instead the direct component, that is because in the future I may be want to reuse the component or change one.

I also soldered the GPS module with the connectors in the wrong direction, that was my fault and I didn't want to, but it works and I don't want to risk to broke it trying to desolder those little bastards! Just solder in the right way and all will be fine!

Here some useful solder video:
Soldering guide for beginner
A video about desolder

Adafruit Youtube channel, a lot of interesting stuff there!

When you solder, try to use just the amount of metal you need, otherwise you're going to do a mess. Don't be afraid of doing it, maybe start with something not so expensive, and than keep solder different stuff. The right material makes also the difference!

Step 2: The Sketch!

First, of course, we import the library and build their objects to work with: SPI.h is for communicate with SPI modules, SD is the MicroSD library and Adafruit_GPS is the library of the GPS module. SoftwareSerial.h is for creating a serial port via software. The syntax is "mySerial(TxPin, RxPin);". The GPS object need to be pointed to a serial (in the brackets).
Here's the libraries' links for Adafruit GPS breakout, the MicroSD breakout (to do a clean job you should also format the SD with this software from SD association) and the Software Serial library (it should be included in the IDE).

NOTE: I faced some problem when trying to append a lot of information in one file or using more than two files in the sketch. I did not formatted the SD with that software, maybe that could solve the problem. Also, I tried to add another sensor in the device, a BMP280 (I2C module), without any success. It seems like using I2C module makes the sketch going crazy! I already aked about it in the Adafruit forum, but I still got no answer.

#include "SPI.h"<br>#include "SD.h"<br>#include "Adafruit_GPS.h"<br>#include "SoftwareSerial.h"

SoftwareSerial mySerial(3,2);
Adafruit_GPS GPS(&mySerial);

Now we need all our variables: The two strings are for reading the two sentences that we need to compute a bunch of useful information from the GPS. The char is for stock the sentences before parse them, the floats are for calculate the coordinates in degrees (GPS send use coordinates in degrees and minutes, we need them in degrees for let the read in google earth).
The chipSelect is the pin where we plug the CS of the MicroSD card. In this case is D4, but if you're using an SD shield, you'll have to put D10 here.
File variable is the one who will stock the information of the file we are using during the sketch.

String NMEA1;
String NMEA2;
char c; 
float deg;
float degWhole;
float degDec;

int chipSelect = 4;
File mySensorData;

Now we're declaring a couple fo functions to make the sketch a bit more elegant and less messy:

They're doing basically the same: reading NMEA sentences.
clearGPS() is ignoring three sentences and readGPS() is saving two of them in the variables.

Let's see how: A while loop is controlling if there is new NMEA sentences on the module and reading the GPS stream until there is one. When a new sentence is there, we're out the while loop, where the sentence is actually read, parsed and stocked in the first NMEA variables. We are immediately doing the same for the next one, because the GPS is constantly streaming, it's not waiting for us to be ready, we got no time to print it immediately

This is very important! Don't do anything before you stock both sentences, otherwise the second would eventually be corrupted or just wrong.

After we got two sentences, we print them in the serial to control that is going good.

void readGPS() { 
	clearGPS();   
  while(!GPS.newNMEAreceived()) {
    c=GPS.read();
  }
  GPS.parse(GPS.lastNMEA());
  NMEA1=GPS.lastNMEA();

while(!GPS.newNMEAreceived()) {
    c=GPS.read();
  }
  GPS.parse(GPS.lastNMEA());
  NMEA2=GPS.lastNMEA();

Serial.println(NMEA1);
Serial.println(NMEA2);
}

void clearGPS() {    
	while(!GPS.newNMEAreceived()) {	<br>	c=GPS.read();<br>	}
  GPS.parse(GPS.lastNMEA());
	<br>	while(!GPS.newNMEAreceived()) {<br>	c=GPS.read();<br>	}
  GPS.parse(GPS.lastNMEA());w

	while(!GPS.newNMEAreceived()) {<br>	c=GPS.read();<br>	}
  GPS.parse(GPS.lastNMEA());
}

Well, now that we are all set, we can get through the setup():

First: we open communication on Serial 115200 for Arduino <--> PC and on 9600 for GPS module <--> Arduino.
Second: we send three commands to the GPS module: the first is to shut the antenna update up, the second is for asking only RMC and GGA string (we are going to use only those, which have all the information you would ever need from a GPS), the third and last command is to set the update rate to 1HZ, suggested by Adafruit.

After that we set the pin D10 to OUTPUT, if, and only if, your SD modele's CS pin is other than D10.
Immediatly after, se set the CS on the SD module on the chipSelect pin.

We run the functions readGPS() that include the cleanGPS().

Now it's time to write something in the files! If the file is already in the Sd card, append a timestamp on them. In this way we don't have to keep track of the sessions or erase the files every time. With a timestamp written within the setup function, we are sure to just add a separation in the files just once per session.

NOTE: The SD library is pretty serious about open and close the file every time! Keep it in mind and close it every time! To learn about the library follow this link.

Ok, we're really all set to get the core of the stream-and-log part of the sketch.

void setup() {  
Serial.begin(115200);
  GPS.begin(9600);

//Send commands to the GPS module
  GPS.sendCommand("$PGCMD,33,0*6D");
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
  delay(1000); 
  
//only if your SD module's CS pin isn't on pin D10</p><p>pinMode(10, OUTPUT); 
  SD.begin(chipSelect);  

readGPS();
if (SD.exists("NMEA.txt")) {
    mySensorData = SD.open("NMEA.txt", FILE_WRITE);
    mySensorData.println("<----------------------- Nuova sessione ----------------------->");
    mySensorData.print("***  ");
    mySensorData.print(GPS.day);
    mySensorData.print(".");
    mySensorData.print(GPS.month);
    mySensorData.print(".");
    mySensorData.print(GPS.year);
    mySensorData.print(" -- ");
    mySensorData.print(GPS.hour);
    mySensorData.print(":");
    mySensorData.print(GPS.minute);
    mySensorData.print(":");
    mySensorData.print(GPS.seconds);
    mySensorData.println("  ***");
    mySensorData.close();
  }
  if (SD.exists("GPSData.txt")) {
    mySensorData = SD.open("GPSData.txt", FILE_WRITE);
    mySensorData.println("");
    mySensorData.println("<----------------------- Nuova sessione ----------------------->");
    mySensorData.print("***  ");
    mySensorData.print(GPS.day);
    mySensorData.print(".");
    mySensorData.print(GPS.month);
    mySensorData.print(".");
    mySensorData.print(GPS.year);
    mySensorData.print(" -- ");
    mySensorData.print(GPS.hour);
    mySensorData.print(":");
    mySensorData.print(GPS.minute);
    mySensorData.print(":");
    mySensorData.print(GPS.seconds);
    mySensorData.println("  ***");
    mySensorData.close();
  }<br>}

Now we're getting the core of the sketch.

It's super simple, indeed.

We're going to read the GPS stream with the readGPS() function, than we control if we have a fix equal to 1, that mean that we're connected with a satellite.
If we got it, we're going to write our infos in the files. In the first file "NMEA.txt", we write only the raw sentences. In the second file, "GPDData.txt", we append the coordinates (converted with the functions we saw before) and the altitude.
Those informations are enough to compile a .kml file to create a path on Google Earth. Note that we close the files everytime we opened it to write something!

void loop() {
readGPS();
//  Condizione if che controlla se l'antenna ha segnale. Se si, procede con la scrittura dei dati.
if(GPS.fix==1) { //Only save data if we have a fix
  mySensorData = SD.open("NMEA.txt", FILE_WRITE); //Apre il file per le frasi NMEA grezze
  mySensorData.println(NMEA1);                    //Scrive prima NMEA sul file
  mySensorData.println(NMEA2);                    //Scrive seconda NMEA sul file
  mySensorData.close();                           //Chiude file!!</p><p>  mySensorData = SD.open("GPSData.txt", FILE_WRITE);
//  Converte e scrive la longitudine
  convLong();
  mySensorData.print(deg,4);      //  Scrive le coordinate in gradi sul file
  mySensorData.print(",");        //  Scrive una virgola per separare i dati
  Serial.print(deg);
  Serial.print(",");
//  Converte e scrive la latitudine
  convLati();
  mySensorData.print(deg,4);      //  Scrive le coordinate in gradi sul file
  mySensorData.print(",");        //  Scrive una virgola per separare i dati
  Serial.print(deg);
  Serial.print(",");
//  Scrive l'altitudine
  mySensorData.print(GPS.altitude);
  mySensorData.print(" ");
  Serial.println(GPS.altitude);
  mySensorData.close();
}
}

Now that we're all done, you can upload the sketch, build the device and enjoy it!

Note that you need to use it with the GPS borad facing the sky in order to get a fix=1, or you can plug an external antenna to it.

Also, keep in mind that if have a fix, the red light is blinking every 15 seconds, if you don't, much faster (once every 2-3 seconds).

If you want to learn something more about the NMEA sentences, just follow the next step of this guide.

Step 3: The NMEA Sentences and the .kml File

The device and the sketch are complete, they're working fine. Keep in mind that to get a fix (to have a connection with satellites) the breakout should face the sky.

The little red light blinks every 15 seconds when you got a fix.

If you want to understand better the NMEA sentences, you could read further.

In the sketch we use only two sentences, the GGA and the RMC. They're just a couple of the sentences that the GPS module is streaming.

Let's see what's in those string:

$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A

RMC = Recommended Minimum sentence C
123519 = Fix taken at 12:35:19 UTC
A = Status A=active or V=Void
4807.038,N = Latitude 48 deg 07.038' N
01131.000,E = Longitude 11 deg 31.000' E
022.4 = Speed over the ground in knots
084.4 = Track angle in degrees True
230394 = Date - 23rd of March 1994
003.1,W = Magnetic Variation
*6A = The checksum data, always begins with *

$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47

GGA Global Positioning System Fix Data
123519 Fix taken at 12:35:19 UTC
4807.038,N Latitude 48 deg 07.038' N
01131.000,E Longitude 11 deg 31.000' E
1 Fix quality: 0 = invalid; 1 = GPS fix (SPS);2 = DGPS fix; 3 = PPS fix; 4 = Real Time Kinematic; 5 = Float RTK; 6 = estimated (dead reckoning) (2.3 feature); 7 = Manual input mode; 8 = Simulation mode; 08 Number of satellites being tracked
0.9 Horizontal dilution of position
545.4,M Altitude, Meters, above mean sea level
46.9,M Height of geoid (mean sea level) above WGS84 ellipsoid
(empty field) time in seconds since last DGPS update
(empty field) DGPS station ID number
*47 the checksum data, always begins with *

As you can see, there are much more information that what you need there. Using the Adafruit's library, you can call some of them, like GPS.latitude or GPS.lat (latitude and lat hemisphere), or GPS.day/month/year/hour/minute/seconds/milliseconds...
Take a look to the Adafruit website to know something more. Is not so clear, but following some hints in the GPS modules' guide, you could find what you need.

What we can do with files we've got?
Easy: compile a kml file to show a path on Google Earth. In order to do it, just copy/past the code you'll find following this link (under the paragraph Path), put your coordinates from the GPDData.txt file between the tags, save the file with .kml extension and load it on Google Earth.

NOTE: The .kml markup language is simple, if you already know what a markup language is, keep your time to read the previous link and documentation inside, it's actually interesting!

Using the kml is all about know its tags and arguments. I found only the guide from Google, the one I linked before and the essential part is to define the style between the < style > tags and call it with # sign when it's time to write the coordinates.

The file I added in this section is a .kml in which you could just paste your coordinates. keep in mind to paste with this syntax: longitude,latitude,altitude