GPS module Interfacing with PIC18F4550

Interfaced GPS receiver module with PIC18F4550 and display the Time, Latitude, Longitude, and Altitude on LCD20x4 display. PIC18F4550 read the data serially from the GPS receiver using USART communication with 9600 Baud rate.

20.2k views6 min readUpdated Aug 8, 2023

Overview of GPS

Global Positioning System (GPS) makes use of signals sent by satellites in space and ground stations on Earth to accurately determine their position on Earth.

Radio Frequency signals sent from satellites and ground stations are received by the GPS. GPS makes use of these signals to determine its exact position.

The GPS itself does not need to transmit any information.

The signals received from the satellites and ground stations contain timestamps of the time when the signals were transmitted. By calculating the time difference between the time the signal was transmitted and the time the signal was received, and using the speed of the signal, the distance between the satellites and the GPS can be determined using a simple formula for distance using speed and time.

Using information from 3 or more satellites, the exact position of the GPS can be triangulated.

For more information about GPS and how to use it, refer to the topic GPS Receiver Module in the sensors and modules section.

The GPS receiver module uses USART communication to communicate with the controller or PC terminal.

For information about USART in PIC18F4550 and how to use it, refer to the topic USART in PIC18F4550 in the PIC inside section.

This is the picture of GPS Receiver Module
GPS Receiver Module

 

Connection Diagram GPS to PIC18F4550

This is the picture of GPS Receiver Interfacing with PIC microcontroller
GPS Receiver Interfacing with PIC18F4550 

 

Get Lat, Long, Alt, and UTC using PIC18F4550 

Now let’s interface the GPS receiver module with PIC18F4550 and display the Time, Latitude, Longitude, and Altitude on the LCD20x4 display.

In this interfacing, the PIC18F4550 microcontroller will read data serially from the GPS receiver using USART communication with a 9600 Baud rate.

Then parse the “$GPGGA” string to extract information regarding time, latitude, longitude, and altitude.

 

GPS Code for PIC18F4550

/*
    GPS Information extraction using PIC18F4550 
    http://www.electronicwings.com
*/

#include<pic18f4550.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include "Configuration_Header_File.h"
#include "LCD_20x4_H_file.h"
#include "USART_Header_File.h"

unsigned long int get_gpstime();
float get_latitude(unsigned char);
float get_longitude(unsigned char);
float get_altitude(unsigned char);
void convert_time_to_UTC(unsigned long int);
float convert_to_degrees(float);

#define GGA_Buffer_Size 80
#define GGA_Pointers_Size 20

char GGA_Buffer[GGA_Buffer_Size];              /* to store GGA string */
char GGA_CODE[3];

unsigned char N_S, E_W;                        /* for getting direction polarity */
unsigned char GGA_Pointers[GGA_Pointers_Size]; /* to store instances of ',' */
char CommaCounter;
char Data_Buffer[15];
volatile unsigned int GGA_Index;
volatile unsigned char	IsItGGAString	= 0;

void main(void) {
	unsigned long int Time;
	float Latitude,Longitude,Altitude;
	char GPS_Buffer[15];
    
	OSCCON = 0x72;      /* use internal osc. of 8MHz Freq. */
	LCD_Init();
	INTCONbits.GIE=1;   /* enable Global Interrupt */
	INTCONbits.PEIE=1;  /* enable Peripheral Interrupt */
	PIE1bits.RCIE=1;    /* enable Receive Interrupt */
	USART_Init(9600);
	
	while(1){
	memset(GPS_Buffer,0,15);
	LCD_String_xy(1,0,"UTC Time: ");
	Time = get_gpstime();            /* Extract Time */
	convert_time_to_UTC(Time);       /* convert time to UTC */
	LCD_String(Data_Buffer);
	LCD_String("  ");
		
	LCD_String_xy(2,0,"Lat: ");
	Latitude = get_latitude(GGA_Pointers[0]); 	/* Extract Latitude */
	Latitude = convert_to_degrees(Latitude);  	/* convert raw latitude in degree decimal*/
	sprintf(GPS_Buffer,"%.05f",Latitude);		/* convert float value to string */
	LCD_String(GPS_Buffer);            			/* display latitude in degree */
	memset(GPS_Buffer,0,15);
		
	LCD_String_xy(3,0,"Long: ");
	Longitude = get_longitude(GGA_Pointers[2]);	/* Extract Latitude */
	Longitude = convert_to_degrees(Longitude);	/* convert raw longitude in degree decimal*/
	sprintf(GPS_Buffer,"%.05f",Longitude);		/* convert float value to string */
	LCD_String(GPS_Buffer);            			/* display latitude in degree */
	memset(GPS_Buffer,0,15);
			
	LCD_String_xy(4,0,"Alt: ");
	Altitude = get_altitude(GGA_Pointers[7]); 	/* Extract Latitude */
	sprintf(GPS_Buffer,"%.2f",Altitude);		/* convert float value to string */
	LCD_String(GPS_Buffer);            			/* display latitude in degree */

	}
}

unsigned long int get_gpstime(){
	unsigned char index;
	unsigned char Time_Buffer[15];
	unsigned long int _Time;
	
	/* parse Time in GGA string stored in buffer */
	for(index = 0;GGA_Buffer[index]!=','; index++){		
		Time_Buffer[index] = GGA_Buffer[index];
	}
	_Time= atol(Time_Buffer);        /* convert string of Time to integer */
	return _Time;                    /* return integer raw value of Time */        
}

float get_latitude(char lat_pointer){
	unsigned char lat_index = lat_pointer+1;	/* index pointing to the latitude */
	unsigned char index = 0;
	char Lat_Buffer[15];
	float _latitude;

	/* parse Latitude in GGA string stored in buffer */
	for(;GGA_Buffer[lat_index]!=',';lat_index++){
		Lat_Buffer[index]= GGA_Buffer[lat_index];
		index++;
	}
	lat_index++;
	N_S = GGA_Buffer[lat_index];
	_latitude = atof(Lat_Buffer);     /* convert string of latitude to float */
	return _latitude;                 /* return float raw value of Latitude */
}

float get_longitude(unsigned char long_pointer){
	unsigned char long_index;
	unsigned char index = long_pointer+1;		/* index pointing to the longitude */
	char Long_Buffer[15];
	float _longitude;
	long_index=0;
	
	/* parse Longitude in GGA string stored in buffer */
	for( ; GGA_Buffer[index]!=','; index++){
		Long_Buffer[long_index]= GGA_Buffer[index];
		long_index++;
	}
	long_index++;
	E_W = GGA_Buffer[long_index];
	_longitude = atof(Long_Buffer);    /* convert string of longitude to float */
	return _longitude;                 /* return float raw value of Longitude */
}

float get_altitude(unsigned char alt_pointer){
	unsigned char alt_index;
	unsigned char index = alt_pointer+1;		/* index pointing to the altitude */
	char Alt_Buffer[12];
	float _Altitude;
	alt_index=0;
	
	/* parse Altitude in GGA string stored in buffer */
	for( ; GGA_Buffer[index]!=','; index++){
		Alt_Buffer[alt_index]= GGA_Buffer[index];
		alt_index++;
	}
	_Altitude = atof(Alt_Buffer);   /* convert string of altitude to float */ 
	return _Altitude;					/* return float raw value of Altitude */
}

void convert_time_to_UTC(unsigned long int UTC_Time)
{
	unsigned int hour, min, sec;
		
	hour = (UTC_Time / 10000);                  	/* extract hour from integer */
	min = (UTC_Time % 10000) / 100;             	/* extract minute from integer */
	sec = (UTC_Time % 10000) % 100;             	/* extract second from integer*/

	sprintf(Data_Buffer, "%d:%d:%d", hour,min,sec); /* store UTC time in buffer */
	
}

float convert_to_degrees(float NMEA_lat_long){
	
	float minutes, dec_deg, decimal;
	int degrees;
	float position;

	degrees = (int)(NMEA_lat_long/100.00);
	minutes = NMEA_lat_long - degrees*100.00;
	dec_deg = minutes / 60.00;
	decimal = degrees + dec_deg;
	if (N_S == 'S' || E_W == 'W') { // return negative
		decimal *= -1;
    }	
	/* convert raw latitude/longitude into degree format */
	return decimal;
}

void interrupt Serial_ISR()   
{
	 
	if(RCIF){
		GIE  = 0;							/* Disable global interrupt */
		unsigned char received_char = RCREG;
        if(RCSTAbits.OERR){                 /* check if any overrun occur due to continuous reception */           
            CREN = 0;
            NOP();
            CREN=1;
        }
        
		if(received_char =='$'){     	    /* check for '$' */
			GGA_Index = 0;
			IsItGGAString = 0;
			CommaCounter = 0;
		}
		else if(IsItGGAString == 1){        /* if true save GGA info. into buffer */
			if(received_char == ',' ) GGA_Pointers[CommaCounter++] = GGA_Index;    /* store instances of ',' in buffer */
			GGA_Buffer[GGA_Index++] = received_char;
        }
		else if(GGA_CODE[0] == 'G' && GGA_CODE[1] == 'G' && GGA_CODE[2] == 'A'){ /* check for GGA string */
			IsItGGAString = 1;
			GGA_CODE[0] = 0; GGA_CODE[1] = 0; GGA_CODE[2] = 0;	
		}
		else{
			GGA_CODE[0] = GGA_CODE[1];  GGA_CODE[1] = GGA_CODE[2]; GGA_CODE[2] = received_char; 
		}	
	}
}

 

How to Calculate Latitude and Longitude in GPS coordinates form

We get Latitude and Longitude from GGA string which is in the form of ddmm.mmmm and dddmm.mmmm respectively.

Where,

D – degree

M – minutes

Now, we can convert received latitude and longitude string in DMS (Degree Minute Second) and Degree Decimal.

DMS – [dd] degree, [mm] minutes, [(.mmmm)*60] seconds

Degree Decimal– [dd] degree + (mm.mmmm/60)

E.g. We have the following Lat/Long data in NMEA format

Latitude – 1829.9639

Longitude – 07347.6174

Now, convert them in the following format –

DMS – 18° degree 29 minutes 57.834 seconds

Degree -  18.499398

 

Video of GPS Communication with PIC18F4550

Components Used

Powered byMouser Electronics

Downloads

Comments21

Join the discussion — share a question or tip.

  • DM
    DmitrijDmitrij· edited

    Thanks for the description! I built a GPS Tracker, Link: https://pic-projekte.de/forum/viewtopic.php?f=4&amp;t=143

  • NG
    NguyenTri

    In Interrupt Service Routine function you clear GIE bit (global interrupt) to disable USART interrupt, this case also to disable all other interrupts. How to write a program with more than one interrupt function and they run independently.

  • BL
    blarblublublar

    Latitude = get_latitude(GGA_Pointers[0]); Longitude = get_longitude(GGA_Pointers[2]); Altitude = get_altitude(GGA_Pointers[7]); Hello I think GGA_Pointers index shoulde be Latitude = get_latitude(GGA_Pointers[1]); Longitude = get_longitude(GGA_Pointers[3]); Altitude = get_altitude(GGA_Pointers[8]); Am i Right?

  • EG
    egondoidao

    Hello there! Awesome project lokeshc! I'm wondering, is it possible to use a 16x2 display instead? It shouldn't be too hard to adapt right?

    • LO
      lokeshc

      yes, it is possible to use LCD 16x2. But, LCD 20x4 is also not so different than LCD 16x2.

  • SI
    siv12345

    Sir, need PIC18F4550 gps speed monitoring code. please send to my mail id sivaeshwaran@gmail.com, this gps module is very great to monitor high accuracy location.

    • LO
      lokeshc

      You can extract speed information from NMEA 'RMC' and 'VTG' string. It is same as extracting latitude, longitude from gga string.

    • RO
      Robert

      Replying to @lokeshc

      Hello Lokeshc, You mentioned " changes are minor regarding float, double and int conversions. ". Would you please send me updated code? Thank you. roberttku5178@gmail.com

    • RO
      Robert

      Replying to @Robert

      Hello Lokeshc, I followed your code, but can't see any output on LCD. Would you please teach me, why? Robert

    • LO
      lokeshc

      Replying to @Robert

      there may be lots of reasons for not getting any output. check connections properly. did you use that code for lcd20x4 or lcd16x2? debug the code by displaying some test character on LCD which will help you to find any error if any?

  • IS
    Isteward

    You can correct the accuracy by setting the following project configuration. Project Properties -&gt;  XC8 Global options -&gt; XC8 linker -&gt; Options categories -&gt; Memory model -&gt; set float and double to 32 bits.

    • LO
      lokeshc

      Cool!!! I will try it. Ty in advance.

    • CH

      Replying to @lokeshc

      Hi, I had the same problem with accuracy when trying to convert to British National Grid (complicated maths). The fault is in the float definition. "So, for example, if you are using a 24-bit wide floating-point type, it can exactly store the value 95000.0. However, the next highest number it can represent is 95002.0 and it is impossible to represent any value in between these two in such a type as it will be rounded. " Full info in the XC8 manual Section 5.4.3 pages 147/8 Even after setting float and double to 32 bits, it was causing 100m inaccuracy in the conversion. Only option is to step up the processor range.

  • IS
    Isteward

    Can someone please respond to my last question. Usually I get an automated email when I ask a question but not this time. Can you respond please? Ian

    • LO
      lokeshc

      Ian Steward Hey Ian, I want to know how you did the 32-bit xc8 linker settings. you can use mpu6050 and GPS combined. While accessing mpu6050 data you can disable interrupt and re-enable it after reading data. To do this, you can search on google.

  • IS
    Isteward

    Hello Lokeshc I was able to get the correct accuracy by going to 32 bits via xc8 linker settings. Thanks for your help on this. I now have another question regarding the GPS project. I have been trying for a month now to configure your GPS project to include your Magnetometer project code so that I can access GPS data and Heading Data on the single pic18f4550. I think it involves Usart and turning off and on the interrupt flags but so far I have been unable to successfully have them coexist. Can you give me some guidance on doing this. I can send you my code if you wish. Please let me know if you could help because I have had expanded a great deal of time on this. Thank you.

  • IS
    Isteward

    I was hoping for a response to my last two messages. It is very disappointing that I can get no corrections to the gps code you supplied. Please advise that you will no longer provide assistance with this. It is very disappointing that you leave me without an answer. Please advise. Ian Steward

  • IS
    Isteward

    I have tried without success to get the software to reflect accurately from the raw NMEA data. Can you offer any code alternatives to make this work? Thanks Ian

  • IS
    Isteward

    Hello Thank you for quick response. I looked into your changes. There was no change in result with Latitude. The changes made Longitude inaccurate - it was accurate before the change. At first I suspected that the raw NMEA data was not being read correctly but I confirmed that it was ok. So I determined that the issue was the atof function. An input of char 4302.39450 gave an incorrect output result of float 4302.4730 and the convertion from raw to degrees probably added a little more error but not much. I have tried a number of alternatives to atof but could not make any of them compile in xc8. Let me know if you can recommend any code alternatives to give me the correct answers. Again thank you for help on this and the time you have spent on it. Ian

  • IS
    Isteward

    I have used many of your circuits with no issue. They are concise, accurate and well documented. However I do have an issue with the GPS module Interfacing with PIC18F4550. I plan to use the GPS module as part of my project to guide an AGV car. Here is my issue. The Latitude calculated in your code is inaccurate and I cannot resolve the issue. In my current GPS location I verified that the Latitude coordinate should be 43.03993 but the software is calculating 43.04199. First of all I am using the documented circuit and XC8 code in your article, including the same GPS module you used. The NMEA code should 4302.3999 which should give me Lat 43.0399. Longitude does not appear to be an issue. I also ran an NMEA list from the same module with an arduino Mega and the average Lat code was 4302.3994 and this equates to my actual Lat coordinate. I also duplicated this testing with a different GPS module (same model) with the same result. I have to assume that your project would pull from the same NMEA data - so why is there a discrepancy? I also did some checking on the NMEA Latitude code in your code which appeared to be close to 4302.4370 and this does not even equate to 43.04199 on the LCD display. I have also tried troubleshooting using your code in a c program and the results indicate that your xc8 code is not operating as the c program - anyway this is not an ideal testing. Just to confirm that I do not move locations in my testing. In my calculation the displayed Latitude coordinate is approximately 1200 from the correct one. As I said Longitude doesn’t appear to be a problem. Can you help to resolve this issue because it would make my project unworkable. By the way I also reached out on another issue in February and received an email saying that ElectronicWings would get back to me, no one ever did get back to me. Please help me or suggest another way to resolve this. Please don’t leave me hanging. Ian Steward Email: Ian.steward@comcast.net Home Phone: 603-384-1219 Cell Phone: 603-660-3413 Located in New Hampshire, USA

    • LO
      lokeshc

      Hello Ian, I made some changes in above program so you can use it. The changes are minor regarding float, double and int conversions. Also use Pole( North, South, East, West ) information to convert latitude and longitude into degrees. Check this program with your GPS module hope it will work. And, if doesn't work then let me know. I found that PIC microcontroller not giving accurate float value beyond 3 places after a decimal. But, if you find anything regarding float value or any mistake in a new program then also let me know.