GPS Tracker with Google Maps (Adv.)

INTRODUCTION 

The idea behind this project is to construct a low-cost portable GPS tracker that can display the position in real time on Google Maps. I didn’t have a specific use for such a device, it was mainly done in the spirit of learning, but now that I built one, I can think of many uses for it.

HOW DOES IT WORK?

In a nutshell, this is how the GPS Tracker works. The GPS chip outputs the positioning information which is transferred over a GPRS link to a remote server over a TCP connection. The TCP server stores the incoming positional data in a mySQL database. When a user clicks on the tracking page, Zope, which is an open source web application server, serves up an HTML page with an embedded javascript code. The javascript would run in the user’s browser and has instructions to retrieve the positional information from the mySQL database every second. It then integrates this information into Google Maps through Google Maps API which displays the position on a map. Since the positional information is retrieved every second and the maps updated at the same frequency, a real time GPS tracking effect is acheived.

HARDWARE

The required hardware can be broadly divided into the following modules:

  1. Micro-Controller
  2. GSM/GPRS Module
  3. GPS Module
  4. Power Module

MICRO-CONTROLLER

The micro-controller is the central controller for the whole unit. I chose the Arduino Duemilanove, which is an open source electronics prototyping board based on the Atmel ATMega328 8 bit micro-controller running at 16 Mhz. I would have liked more hardware serial ports (the Duemilanove has one), but I got around using a software serial library.

GSM/GPRS Module

The GSM/GPRS Module is based on SpreadTrum’s SM5100B. The SM5100B is a miniature, quad-band GSM 850/EGSM 900/DCS 1800/PCS 1900 module. This module features two UARTS, an SPI interface, and two 10-bit ADC’s.

GPS Module

The GPS Module is USGlobalSat EM-406A which is based on the spectacular SiRF Star III chipset. It outputs positioning and timing date in NMEA 0183 and SiRF binary protocol and has a positioning accuracy of 10 meters without and 5 meters with WAAS.


POWER

For power, I’m using a 9V battery for now. I’m planning to include a voltage-regulator based 5V power system, but for now the battery suffices.

SOFTWARE

  • ARDUINO SKETCH

This code is for the ATMega328P microcontroller on the Arduino board. The following should be noted:

The line:

cell.println("AT+CGDCONT=1,\"IP\",\"isp.cingular\"");
establishes a PDP (Packet Data Protocol) context with AT&T's APN (Access Point Name). 
If your cellular provider is not AT&T, replace the string "isp.cingular" 
with the appropriate APN for your cellular provider.

The line:

cell.println("AT+SDATACONF=1,\"TCP\",\"109.2.29.187\",32000");

assumes the TCP Server is listening on IP Address: 109.2.29.187 on port 32000. 
Change this to the correct value for your TCP Server.
#include <NewSoftSerial.h>  //Include the NewSoftSerial library to send serial commands to the cellular module.
#include <TinyGPS.h>
#include <PString.h>

#define POWERPIN 4
#define GPSRATE 4800
#define BUFFSIZ 90

char at_buffer[BUFFSIZ];
char buffidx;

int firstTimeInLoop = 1;

int GPRS_registered;
int GPRS_AT_ready;

char incoming_char=0;      //Will hold the incoming character from the Serial Port.
char buffer[60];

PString myString(buffer,sizeof(buffer));

NewSoftSerial cell(2,3);  //Create a 'fake' serial port. Pin 2 is the Rx pin, pin 3 is the Tx pin.
TinyGPS gps;

int redLedPin  = 11;
int blueLedPin = 12;

// Function to Blink a LED
// Parameters: lPin   - Pin of the LED
//             nBlink - Number of Times to Blink
//             msec   - Time in milliseconds between each blink

void blinkLed(int lPin, int nBlink, int msec) {

    if (nBlink) {
        for (int i = 0; i < nBlink; i++) {
            digitalWrite(lPin, HIGH);
            delay(msec);
            digitalWrite(lPin, LOW);
            delay(msec);
        }
    }
}

// Function to Switch on a LED
// Parameters: lPin - Pin of the LED

void onLed (int lPin) {
    digitalWrite(lPin, HIGH);
}

// Function to Switch off a LED
// Parameters: lPin - Pin of the LED

void offLed (int lPin) {
    digitalWrite(lPin, LOW);
}

// Do system wide initialization here in this function
void setup()
{

    // LED Pin are outputs. Switch the mode 

    pinMode(redLedPin, OUTPUT);
    pinMode(blueLedPin, OUTPUT);

    /* Blink the Power LED */

blinkLed(redLedPin,3,500);

    //Initialize serial ports for communication.

    Serial.begin(4800);
    cell.begin(9600);

//Let's get started!

    Serial.println("Starting SM5100B Communication...");

    delay(5000);

    /* Currently GPRS is not registered and AT is not ready */

    GPRS_registered = 0;
    GPRS_AT_ready = 0;

}

/* Reads AT String from the SM5100B GSM/GPRS Module */

void readATString(void) {

    char c;
    buffidx= 0; // start at begninning
    while (1) {
        if(cell.available() > 0) {
            c=cell.read();
            if (c == -1) {
                at_buffer[buffidx] = '';
                return;
            }

            if (c == '\n') {
                continue;

            }

            if ((buffidx == BUFFSIZ - 1) || (c == '\r')){
                at_buffer[buffidx] = '';
                return;
            }

            at_buffer[buffidx++]= c;
        }
    }
}

/* Processes the AT String to determine if GPRS is registered and AT is ready */

void ProcessATString() {

    if( strstr(at_buffer, "+SIND: 8") != 0 ) {
        GPRS_registered = 0;
        Serial.println("GPRS Network Not Available");
    }

    if( strstr(at_buffer, "+SIND: 11") != 0 ) {
        GPRS_registered=1;
        Serial.println("GPRS Registered");
        blinkLed(redLedPin,5,100);

    }

    if( strstr(at_buffer, "+SIND: 4") != 0 ) {
        GPRS_AT_ready=1;
        Serial.println("GPRS AT Ready");
    }

}

void loop() {

    /* If called for the first time, loop until GPRS and AT is ready */

    if(firstTimeInLoop) {
        firstTimeInLoop = 0;
        while (GPRS_registered == 0 || GPRS_AT_ready == 0) {
           readATString();
           ProcessATString();

        }

        if(POWERPIN) {
            pinMode(POWERPIN, OUTPUT);
        }

        pinMode(13, OUTPUT);

        Serial.println("GPS Parser Initialized");
        digitalWrite(POWERPIN, LOW);
        delay(1000);
        Serial.println("Setting up PDP Context");
        cell.println("AT+CGDCONT=1,\"IP\",\"isp.cingular\"");
        delay(1000);
        Serial.println("Activating PDP Context");
        cell.println("AT+CGACT=1,1");
        delay(1000);
        Serial.println("Configuring TCP connection to TCP Server");
        cell.println("AT+SDATACONF=1,\"TCP\",\"109.2.29.187\",32000");
        delay(1000);
        Serial.println("Starting TCP Connection\n");
        cell.println("AT+SDATASTART=1,1");

        onLed(redLedPin);

    } else {

            while(Serial.available()) {
                int c = Serial.read();
                if (gps.encode(c)) {
                    onLed(blueLedPin);
                    float flat, flon;
                    unsigned long fix_age;
                    gps.f_get_position(&flat,&flon,&fix_age);
                    if(fix_age == TinyGPS::GPS_INVALID_AGE)
                        Serial.println("No fix detected");
                    else if (fix_age > 5000)
                        Serial.println("WARNING: Possible Stale Data!");
                    else {
                        myString.print("AT+SSTRSEND=1,\"");
                        myString.print("Lat: ");
                        myString.print(flat,DEC);
                        myString.print(" Long: ");
                        myString.print(flon,DEC);
                        myString.print("\"");
                        Serial.println(myString);
                        cell.println(myString);
                        myString.begin();
                        offLed(blueLedPin);
                   }
             }

        }

    }

}
  • TCP Server Code
The TCP Server is written in Python and is very simple in nature. It uses the mySQLdb module to
communicate with the mySQL server. Since in my case, the TCP Server and the mySQL database
resides in the same machine, it works nicely. You might have to use xmlrpc if the server and the
database are on different machines. 

Note: This code also assumes the IP Address as 109.2.29.187 and port as 32000. 
Change as needed. Similarly, replace your_host, your_user and your_password in the 
call to  mySQLdb.connect

#!/usr/bin/env python

import socket
import MySQLdb

TCP_IP = '109.2.29.187'
TCP_PORT = 32000
BUFFER_SIZE = 40

# ClearDB. Deletes the entire tracking table

def ClearDB(curs,d ):
    curs.execute ("""
                INSERT INTO gmaptracker (lat, lon)
                VALUES (0.0,0.0)""")
    d.commit()

# Connect to the mySQL Database

def tServer():
    try:

        db = MySQLdb.connect (host = "your_host",
                           user = "your_user",
                           passwd = "your_password",
                           db = "gmap" )
    except MySQLdb.Error, e:
        print "Error %d: %s" %(e.args[0], e.args[1])
        sys.exit(1);

    cursor = db.cursor()

    # Start with a fresh tracking table

    ClearDB(cursor,db)

    # Set up listening Socket

    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        s.bind((TCP_IP, TCP_PORT))
        print "Listening...."
        s.listen(1)
        conn, addr = s.accept()
        print 'Accepted connection from address:', addr

    except socket.error (message):
        if s:
            s.close()
            print "Could not open socket: " + message
            cursor.close()
            conn.close()
            db.close()
            sys.exit(1)

    try:
        while 1:
            data = conn.recv(BUFFER_SIZE)
            if not data:break

            str1,str2 = data.split("Long: ")
            str1 = str1.split("Lat: ")[1]
            latitude = float(str1)
            longitude = float(str2)
            cursor.execute ("""
                INSERT INTO gmaptracker (lat, lon)
                VALUES (%s,%s)""", (latitude,longitude))

            db.commit()

    except KeyboardInterrupt:
        ClearDB(cursor,db);
        cursor.close()
        conn.close()
        db.close()

if __name__ == '__main__':
   tServer()
  • MySQL Code

The MySQL code contains instructions to create a database and a table in the database to store the incoming latitude and longitude values.

CREATE DATABASE gmap;

CREATE TABLE `gmaptracker` (
`id` int(8) NOT NULL auto_increment,
`lat` double(12,10) NOT NULL default `0.0000000000`,
`lon` double(12,10) NOT NULL default `0.0000000000`,
PRIMARY KEY (`id`)
) TYPE=InnoDB;

  • Zope Code
  • data.xml (DTML Method)

<?xml version=”1.0″ encoding=”UTF-8″?>
<markers>
<dtml-in GmaplocsPkSelectLastAdded>
<marker lat=”<dtml-var lat>” lng=”<dtml-var lon>”/>
</dtml-in>
</markers>

  • GmaplocsPkSelectLastAdded (Z SQL Method)

This function selects the last row from the gmaptracker table

select * from gmaptracker order by id desc limit 1

  • gpsTrack (HTML with embedded javascript)

This is the file Zope serves to the remote browser. The embedded javascript gets the data.xml file and populates the DTML markers with the latitude and longitude of the last inserted positional information from the mySQL database.

Note:

Replace “your_key_here” text with the Google Maps Key for your application.

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”&gt;
<html xmlns=”http://www.w3.org/1999/xhtml”&gt;
<head>
<title>Real Time GPRS based GPS Tracker</title>
<script src=”http://maps.google.com/maps?
file=api&v=1&key=your_key_here” type=”text/javascript”></script>

<!– Make the document body take up the full screen –>

<style type=”text/css”>
v\:* {behavior:url(#default#VML);}
html, body {width: 100%; height: 100%}
body {margin-top: 0px; margin-right: 0px; margin-left: 0px; margin-bottom: 0px}
</style>

<script type=”text/javascript”>
//<![CDATA[
function load(){
var map = new GMap(document.getElementById(“map”));
var point = new GPoint(0,0);
map.addControl(new GLargeMapControl());
map.addControl(new GMapTypeControl());
map.centerAndZoom(point, 1);
window.setTimeout(function(){reloadMap(map)},1000);
}

function reloadMap(map) {

var request = GXmlHttp.create();
request.open(“GET”, “data.xml”, true);
request.onreadystatechange = function() {
if (request.readyState == 4) {
var xmlDoc = request.responseXML;
var markers = xmlDoc.documentElement.getElementsByTagName(“marker”);
for (var i = 0; i < markers.length; i++) {
var point = new
GPoint(parseFloat(markers[i].getAttribute(“lng”)),
parseFloat(markers[i].getAttribute(“lat”)));
var marker = new GMarker(point);
map.clearOverlays();
map.addOverlay(marker);
map.centerAtLatLng(point);
}
}
}
request.send(null);
window.setTimeout(function(){reloadMap(map)},1000);
}

// Monitor the window resize event and let the map know when it occurs
if (window.attachEvent) {
window.attachEvent(“onresize”, function() {this.map.onResize()} );
} else {
window.addEventListener(“resize”, function() {this.map.onResize()} , false);
}

//]]>
</script>
</head>
<body onload=”load()”>
<div id=”map” style=”width: 100%; height:100%;”></div>

</body>

</html>


CONCLUSION

After wiring everything up, I inserted a SIM card into the GSM Module and powered it up. After the system registered with AT&T’s GPRS network and after it acquired the GPS lock, it began sending the GPS co-ordinates to my TCP-Server, which was running on my local machine and to which I had setup with a port-forwarding rule on my cable modem. The TCP server inserted the GPS co-ordinates into the gmaptracker mySQL database running on the same machine. When I visited the web tracking page, the Zope web application server served up a web page with an embedded javascript file which had instructions to get the latest entry from the gmaptracker table and used Google Maps to display the position. Every second, the javascript code updated the position on the map.


BY JAYESH SUKUMARAN @ http://jayeshprojects.blogspot.com/2010/04/real-time-mobile-gps-tracker-with.html

Leave a comment