GPS Tracker with Google Maps (Adv.)
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:
- Micro-Controller
- GSM/GPRS Module
- GPS Module
- 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.
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”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<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>
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