Arduino GPS

I have been wanting a GPS for a long time, I just find them interesting. I just bought a GPS module from SparkFun along with a 16x2 LCD screen with the sole intention of creating my own along with the help from arduino.
I have owned an arduino for many months yet I had no real projects to engage in due to my lack of money. Now I had the perfect thing to challenge myself and get a working GPS on top of that. I made an oath to myself that I would not use the internet to do the work for me and I succeeded.

Parts:
arduino
GDM1602K LCD
EM-406A GPS Module
err... a button

I have spent about 6-8 hours total over the course of our school vacation and I have to say it was a great learning experience. The whole idea is fairly simple. It reads the data sent by the module and waits for a '$' which signifies the beginning of a protocol line, then it verifies that it is the GPRMC line. The arduino takes the rest of the string and parses out the section you want by using the number of commas that have passed using an implementation String library. I have built in several functions that easily parse and format the data for the LCD using LiquidCrystal library. It uses an interrupt through the button to change pages between: Lat/Lon, Speed/Heading, and Time/Date at the prime meridian.

Any feedback on the code is appreciated!

#include <WString.h>
#include <LiquidCrystal.h>

String chkStr = String(5);
String dataStr = String(65);
String returnStr = String(12);

LiquidCrystal lcd(12, 11, 6, 5, 4, 3);

char chkByte;
char inByte;
int i = 5;
int page = 1;

void setup() {
  attachInterrupt(0, button, RISING);
  Serial.begin(4800);
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("--Arduino  GPS--");
  lcd.setCursor(0, 1);
  lcd.print("------v2.0------");
}

void loop() {
  chkStr = '\0';
  if (Serial.available() > 0) {
    chkByte = Serial.read();
    while (chkByte == '

) {
     if (i > 0) {
       if (Serial.available() > 0) {  
         inByte = Serial.read();
         chkStr.append(inByte);
         i--;
       }
       else {
         waitForByte();
       }
     }
     else {
       i = 5;
       break;
     }
     while (chkStr.equals("GPRMC")) {
       if (Serial.available() > 0) {
         if (dataStr.length() == dataStr.capacity()) {
           break;
         }
         else {
           inByte = Serial.read();
           dataStr.append(inByte);
         }
       }
       else {
         waitForByte();
       }
       if (dataStr.length() == dataStr.capacity()) {
         if(dataStr.charAt(12) == 'A') {
           lcd.clear();
           if (page == 1) {
             printLat(0);
             printLon(1);
             resetData();
             break;
           }
           else if (page == 2) {
             printSpeed(0);
             printHeading(1);
             resetData();
             break;
           }
           else if (page == 3) {
             printTime(0);
             printDate(1);
             resetData();
             break;
           }
         }
         else {
           lcd.clear();
           aquiringFix();
           resetData();
           break;
         }
       }
     }
   }
 }
}

void waitForByte() {
 while (true) {
   if (Serial.available() > 0) {
     break;
   }
   else {
     continue;
   }
 }
}

void aquiringFix() {
 lcd.setCursor(0, 0);
 lcd.print("----Aquiring----");
 lcd.setCursor(0, 1);
 lcd.print("------Fix!------");
}

void button() {
 if (page >= 3) {
   page = 1;
 }
 else {
   page++;
 }
}

void dataParse(int section) {
 char nextChar;
 int commas = 0;
 resetReturn();
 for (int x = 0; x <= dataStr.length(); x++) {
   nextChar = dataStr.charAt(x);
   if (nextChar == ',') {
    commas++;
    continue;
   }
   if (commas == section) {
      returnStr.append(nextChar);
   }
   else if (commas > section) {
     break;
   }
 }
}

void printLat(int row) {
 lcd.setCursor(0, row);
 lcd.print("Lt: ");
 dataParse(4);
 lcd.print(returnStr);
 lcd.print(' ');
 dataParse(3);
 lcd.print('0');
 lcd.print(returnStr.charAt(0));
 lcd.print(returnStr.charAt(1));
 lcd.print(' ');
 lcd.print(returnStr.charAt(2));
 lcd.print(returnStr.charAt(3));
 lcd.print(returnStr.charAt(4));
 lcd.print(returnStr.charAt(5));
 lcd.print(returnStr.charAt(6));
 lcd.print(returnStr.charAt(7));
}

void printLon(int row) {
 lcd.setCursor(0, row);
 lcd.print("Ln: ");
 dataParse(6);
 lcd.print(returnStr);
 lcd.print(' ');
 dataParse(5);
 lcd.print(returnStr.charAt(0));
 lcd.print(returnStr.charAt(1));
 lcd.print(returnStr.charAt(2));
 lcd.print(' ');
 lcd.print(returnStr.charAt(3));
 lcd.print(returnStr.charAt(4));
 lcd.print(returnStr.charAt(5));
 lcd.print(returnStr.charAt(6));
 lcd.print(returnStr.charAt(7));
 lcd.print(returnStr.charAt(8));
}

void printSpeed(int row) {
 lcd.setCursor(0, row);
 dataParse(7);
 lcd.print("Speed: ");
 lcd.print(returnStr);
 lcd.print("Kts");
}

void printHeading(int row) {
 lcd.setCursor(0, row);
 dataParse(8);
 lcd.print("Heading: ");
 lcd.print(returnStr);
}

void printDate(int row) {
 lcd.setCursor(0, row);
 dataParse(9);
 lcd.print("Date: ");
 lcd.print(returnStr.charAt(0));
 lcd.print(returnStr.charAt(1));
 lcd.print("-");
 lcd.print(returnStr.charAt(2));
 lcd.print(returnStr.charAt(3));
 lcd.print("-");
 lcd.print(returnStr.charAt(4));
 lcd.print(returnStr.charAt(5));
}

void printTime(int row) {
 lcd.setCursor(0, row);
 dataParse(1);
 lcd.print("Time: ");
 lcd.print(returnStr.charAt(0));
 lcd.print(returnStr.charAt(1));
 lcd.print(":");
 lcd.print(returnStr.charAt(2));
 lcd.print(returnStr.charAt(3));
 lcd.print(":");
 lcd.print(returnStr.charAt(4));
 lcd.print(returnStr.charAt(5));
}

void resetData() {
 dataStr = '\0';
 Serial.flush();
}

void resetReturn() {
 returnStr = '\0';
}

Here are some pictures. I built a simple cardboard frame to make it easier to work with. I will eventually be putting it in a usable enclosure.

Do you have a photo of the different screens?

That is so neat!!! Keep us posted on all about it - I'm not smart like you - I'll take all the help I can get :-/

Are you receiving NEMA 183 from the GPS? Would it display from other GPS modules if they give NEMA 183 format sentences? $60 is a bit much for a GPS module for me to spend just yet, but if a cheap module would work... it sounds like it's just reading NEMA 183 sentences?

Looks like I need the WString.h library? Is that the only extra I need to compile your code?

Tell us all about it.

Ken H.

What programs are you going to make for your GPS?

Keep up the good Work! :slight_smile:

Mark

BTW, those are good clean, clear photos - you did good!

Ken H>

I found the Wstring.h library but it has ._ in front of several files and does not compile. Any idea what I need to do there? Those ._ should not be there should they?

I've got a GPS module that puts out NEMA 0183 sentences - seems like I've got all the hardware required? I'd like to do that :slight_smile:

Ken H>

@KenH --> As long as the output is in this format I believe it will work fine. Also all i did was download the String library from the Arduino site and unzip it into the libraries folder: String() - Arduino Reference

$GPGGA,161229.487,3723.2475,N,12158.3416,W,1,07,1.0,9.0,M,,,,000018
$GPGSA,A,3,07,02,26,27,09,04,15,,,,,,1.8,1.0,1.5
33
$GPRMC,161229.487,A,3723.2475,N,12158.3416,W,0.13,309.62,120598,,*10

This is a simple program i wrote in the infancy of my GPS to see what the output data looks like. Now keep in mind that the TX pin of the GPS module goes to the RX pin of the arduino. This means you will have to leave the GPS TX pin disconnected while you upload the program or else both the data coming from the GPS and the data being sent by your computer will confuse the microcontroller when it uploads. If you accidentally leave it connected just remove the usb from the arduino, then disconnect the GPS TX pin and restart the upload. This also goes for the full blown GPS program.

void setup(){
  Serial.begin(4800);
}

void loop(){
  if (Serial.available() > 0){
    Serial.write(Serial.read());
  }
}

This will send the data coming directly from the GPS module right to the serial monitor in its raw format.

Another note: When you are not moving the speed and heading readings will be misleading. Due to the slight inaccuracies, especially indoors, the speed and heading is calculated by the satellite and is drawn from your change in lat and long. Therefore any changes in those result in a speed and heading.

...Also, the LCD does displays a degree sign, °, as a dash, -, therefore I don't include it.

Thanks - those do look like standard NEMA 0183 sentences. I WILL have to try that as soon as I get time... maybe next week.

Thank you again - making your own GPS - that's what "Cool" is all about..... or maybe "Geek"?

Ken H>

Here is updated code Version 2.1

All I've done is add the NewSoftSerial library to the code so one does not have to disconnect the GPS transmit every time code is uploaded.

Thanks to Mikal Hart for the NewSoftSerial library and the creators of the WString and LiquidCrystal libraries!

#include <WString.h>
#include <LiquidCrystal.h>
#include <NewSoftSerial.h>

NewSoftSerial gps(8, -1);

String chkStr = String(5);
String dataStr = String(65);
String returnStr = String(12);

LiquidCrystal lcd(12, 11, 6, 5, 4, 3);

char chkByte;
char inByte;
int i = 5;
int page = 1;

void setup() {
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("--Arduino  GPS--");
  lcd.setCursor(0, 1);
  lcd.print("------v2.1------");
  attachInterrupt(0, button, RISING);
  gps.begin(4800);
}

void loop() {
  chkStr = '\0';
  if (gps.available() > 0) {
    chkByte = gps.read();
    while (chkByte == '

) {
     if (i > 0) {
       if (gps.available() > 0) {  
         inByte = gps.read();
         chkStr.append(inByte);
         i--;
       }
       else {
         waitForByte();
       }
     }
     else {
       i = 5;
       break;
     }
     while (chkStr.equals("GPRMC")) {
       if (gps.available() > 0) {
         if (dataStr.length() == dataStr.capacity()) {
           break;
         }
         else {
           inByte = gps.read();
           dataStr.append(inByte);
         }
       }
       else {
         waitForByte();
       }
       if (dataStr.length() == dataStr.capacity()) {
         if(dataStr.charAt(12) == 'A') {
           lcd.clear();
           if (page == 1) {
             printLat(0);
             printLon(1);
             resetData();
             break;
           }
           else if (page == 2) {
             printSpeed(0);
             printHeading(1);
             resetData();
             break;
           }
           else if (page == 3) {
             printTime(0);
             printDate(1);
             resetData();
             break;
           }
         }
         else {
           lcd.clear();
           aquiringFix();
           resetData();
           break;
         }
       }
     }
   }
 }
}

void waitForByte() {
 while (true) {
   if (gps.available() > 0) {
     break;
   }
   else {
     continue;
   }
 }
}

void aquiringFix() {
 lcd.setCursor(0, 0);
 lcd.print("----Aquiring----");
 lcd.setCursor(0, 1);
 lcd.print("------Fix!------");
}

void button() {
 if (page >= 3) {
   page = 1;
 }
 else {
   page++;
 }
}

void dataParse(int section) {
 char nextChar;
 int commas = 0;
 resetReturn();
 for (int x = 0; x <= dataStr.length(); x++) {
   nextChar = dataStr.charAt(x);
   if (nextChar == ',') {
    commas++;
    continue;
   }
   if (commas == section) {
      returnStr.append(nextChar);
   }
   else if (commas > section) {
     break;
   }
 }
}

void printLat(int row) {
 lcd.setCursor(0, row);
 lcd.print("Lt: ");
 dataParse(4);
 lcd.print(returnStr);
 lcd.print(' ');
 dataParse(3);
 lcd.print('0');
 lcd.print(returnStr.charAt(0));
 lcd.print(returnStr.charAt(1));
 lcd.print(' ');
 lcd.print(returnStr.charAt(2));
 lcd.print(returnStr.charAt(3));
 lcd.print(returnStr.charAt(4));
 lcd.print(returnStr.charAt(5));
 lcd.print(returnStr.charAt(6));
 lcd.print(returnStr.charAt(7));
}

void printLon(int row) {
 lcd.setCursor(0, row);
 lcd.print("Ln: ");
 dataParse(6);
 lcd.print(returnStr);
 lcd.print(' ');
 dataParse(5);
 lcd.print(returnStr.charAt(0));
 lcd.print(returnStr.charAt(1));
 lcd.print(returnStr.charAt(2));
 lcd.print(' ');
 lcd.print(returnStr.charAt(3));
 lcd.print(returnStr.charAt(4));
 lcd.print(returnStr.charAt(5));
 lcd.print(returnStr.charAt(6));
 lcd.print(returnStr.charAt(7));
 lcd.print(returnStr.charAt(8));
}

void printSpeed(int row) {
 lcd.setCursor(0, row);
 dataParse(7);
 lcd.print("Speed: ");
 lcd.print(returnStr);
 lcd.print("Kts");
}

void printHeading(int row) {
 lcd.setCursor(0, row);
 dataParse(8);
 lcd.print("Heading: ");
 lcd.print(returnStr);
}

void printDate(int row) {
 lcd.setCursor(0, row);
 dataParse(9);
 lcd.print("Date: ");
 lcd.print(returnStr.charAt(0));
 lcd.print(returnStr.charAt(1));
 lcd.print("-");
 lcd.print(returnStr.charAt(2));
 lcd.print(returnStr.charAt(3));
 lcd.print("-");
 lcd.print(returnStr.charAt(4));
 lcd.print(returnStr.charAt(5));
}

void printTime(int row) {
 lcd.setCursor(0, row);
 dataParse(1);
 lcd.print("Time: ");
 lcd.print(returnStr.charAt(0));
 lcd.print(returnStr.charAt(1));
 lcd.print(":");
 lcd.print(returnStr.charAt(2));
 lcd.print(returnStr.charAt(3));
 lcd.print(":");
 lcd.print(returnStr.charAt(4));
 lcd.print(returnStr.charAt(5));
}

void resetData() {
 dataStr = '\0';
 gps.flush();
}

void resetReturn() {
 returnStr = '\0';
}

Thanks for posting the new stuff - I'm still wanting to do this, but we've had some pretty serious health issues going on the the extended family and I've just not had a chance to work with this any.

I do hope in the next week or 3 I can do this - this sounds "so neat" to actually do this.

[edit]I just downloaded your latest code, got the two libraries installed and got it to compile. Maybe I can work something in over the new week or so? I sure hope so. THANK YOU for sharing your work[/edit]

Ken H>

OUTSTANDING!

This is just what I have been looking for. I'm trying to do the same thing but failed to get any useful data on the LCD.
Just the inspiration (and help) I need :slight_smile:

send 0xDF to the LCD and you will get the degree sign

David

Nice Work!
Your next level should be to make a graphical interface to show compass bearing and direction and maybe waypoints, i've been playing with gps for some time, my last project was a graphical gps with maps downloaded from google maps running on a nintendo gameboy, im in the process of creating a shield for my arduino at the moment

send 0xDF to the LCD and you will get the degree sign

Thanks David - I had been looking for tidbit of info. That will make the "F" look good in a temperature indication I'm using.

Ken H>

Well done ... a great project.
I got a bit gibberish on the screen until I realised you had made a small change to the LCD pin connections in the standard LCD library . No real problem thou.
If I wanted to display the speed in kilometers per hour (Km/h) what would I change ? 1 knot is 1.852Km/h.

cheers

As you are using the LiquidCrystal library, you can also create custom characters, i.e. if you needed to create your own degree sign.

A code creator and graphical editor is available here: http://icontexto.com/charactercreator/

Regards,
Morrolan

Hi my name is Pasquale, i have interest on your display nmea...
but i have difficulty to start with "wString.h" thi is library but i have onli "pString.h" please if you explaine me for solve this problem...

Thanks a lot

Pasquale :smiley:

Not sure if this post is still active but I have a question about your code. I havent used C in YEARS so as I understand the language my ability to actually use the code now is limited.

I was wondering if I just wanted to extract the $GPRMC but not output to an LCD what part of the code would I need? Even though I LOVE what youve done and will probably implement an LCD in my project because this is genius what you have built here.

Any help would be great.

Thanks ;D

any chance of a circuit diagram? i cannot see how u got the button wired up.

my gps communicates using only 2 wires normally, still should be ok i hope.. just a ttl feed from the bluetooth gps mouse.

hoy again, couldnt edit my previos post to add this, but...

for some reason my gps data isnt being recieved by the arduino, no idea why since the data is reading ok on the pc thru the serial monitor, i also sent the data to the arduino from the serial monitor and it displayed 'aquiring fix' meaning its working. so both devices are seeingly ok.
GPS mouse is sending the ttl at 3800 baud, the arduino is listening at 3800 too, but its not responding to the gps data its being fed by the gps, but wen i test via the pc both seem to be doing what they are supposed to do, gps is sending coherent data, arduino is recieving data sent from the pc (manually) but when connected together.. nothing. just the 'arduino gps v2.0' splash screen..

any help would be really good atm cos im a bit stuck, short of runnig the gps to the arduino thru a rs232 to ttl converter to take it from the gps's ttl to rs232 and back to ttl for the arduino, just out of curiosity really..