Mohamed Fadiga
Published © LGPL

Sandro: Your Personal GPS Assistant

Sandro let you monitor things ore people, providing you their position and status.

IntermediateFull instructions provided4,760
Sandro: Your Personal GPS Assistant

Things used in this project

Hardware components

LinkIt ONE
MediaTek Labs LinkIt ONE
×1
Grove Starter Kit for LinkIt ONE
Seeed Grove Starter Kit for LinkIt ONE
×1
Grove starter kit plus for Intel Edison
Seeed Grove starter kit plus for Intel Edison
×1
GPS receiver (generic)
×1
Arduino UNO & Genuino UNO
Arduino UNO & Genuino UNO
×1

Software apps and online services

Google Maps API
PubNub Publish/Subscribe API
PubNub Publish/Subscribe API
jQuery
Chart.js
Arduino IDE
Arduino IDE
ArduinoJson

Story

Read more

Code

Sandro.ino

C/C++
#include <ArduinoJson.h>
#include <LGSM.h>
#include <LBattery.h>
#include <LFlash.h>
#include <LStorage.h>
#include <LGPS.h>
#include <LGPRS.h>
#include <LGPRSClient.h>
#include <LWiFi.h>
#include <LWiFiClient.h>

#define STORAGE LFlash          // Use Internal Flash storage
// #define STORAGE LSD           // Use SD card storage

#define WIFI_AP "AP_name"
#define WIFI_PASSWORD "password"
#define WIFI_AUTH LWIFI_WPA

const char pubnubServer[] = "pubsub.pubnub.com";
const int pubTimeout = 5000, alertTimeout = 300000 ; //timeout for publishing pubnub messages and timout for sending SMS alert
unsigned long pubTime = 0, alertTime = 0;
boolean enable_log = false; //If true, will log data into a file
String subTimetoken = "0"; //Used for set pubnub subscribe timetoken

DynamicJsonBuffer JSONBuffer;
JsonObject& message  = JSONBuffer.createObject(); //JSON object containing data to send trought pubnub
JsonObject& data = JSONBuffer.createObject();  //JSON object containg limit values for distances and sensors

double latitude = 0.00;
double longitude = 0.00;
float altitude = 0.00;
float dop = 100.00;  //Horizontal dilution of position
float geoid = 0.00;  //Height of geoid
float kn_speed = 0.00, kh_speed = 0.00; // Speed in knots and speed in km/h
float track_angle = 0.00;
int fix = 0;
int hour = 0, minute = 0, second = 0;
int sat_num = 0;
int day = 0, month = 0, year = 0;
String time_format = "00:00:00", date_format = "00:00:0000";
String lat_format = "0000.000000", lon_format = "0000.000000";

//LWiFiClient c; //Uncomment this for Wi-Fi connection
LGPRSClient c; //Uncomment this for GPRS connection

const char *phoneNum = "";  //Phone number for SMS and emergency alert
const char *pubKey = "";
const char *subKey = "";
const char *channel = "";
String status = ""; //A string rappresenting the actual status of the device


void setup()
{
  LGPS.powerOn();
  STORAGE.begin();
  LFile myFile = STORAGE.open("settings.json");
  if (myFile)
  {
    String str = "";
    myFile.seek(0);
    while (myFile.available())str += (char)myFile.read();
    myFile.close();
    JsonObject& j = JSONBuffer.parseObject(str);
    phoneNum = j["phoneNum"];
    pubKey = j["pubKey"];
    subKey = j["subKey"];
    channel = j["name"];
    data["places"] = j["places"];
    data["sensors"] = j["sensors"];
    while (!LSMS.ready())delay(1000); //Comment this line if there is not a SIM
    while (!LGPRS.attachGPRS("your_apn", "username", "password")) delay(1000); 
    //while (0 == LWiFi.connect(WIFI_AP, LWiFiLoginInfo(WIFI_AUTH, WIFI_PASSWORD)))delay(1000); //Uncomment this line if you want o use WI-FI
  }
  else while (true);

}

void loop()
{
  status = "";
  checkPlaces();
  checkSensors();
  if (enable_log)logData();
  message["battery_level"] = LBattery.level();
  if (status.length() == 0)status = "OK";
  else
  {
    if (millis() - alertTime > alertTimeout)
    {
      sendSMS(phoneNum , status);
      alertTime = millis();
    }
  }
  if (LSMS.available())
  {
    readSMS();
    LSMS.flush();
  }
  if (millis() - pubTime >= pubTimeout)
  {
    sendData();
    pubTime = millis();
  }
}

void checkSensors()
{
  readSensors();
  for (int i = 0; i < data["sensors"].size(); ++i)
  {
    String name = data["sensors"][i]["name"]; //Sensor name
    int val = message[name]; //sensor value
    if (val > data["sensors"][i]["max"] || val < data["sensors"][i]["min"]) //If i we are over a limit
    {
      status += name;
      status += " allarm: ";
      status += val;
      status += ". ";
    }
  }
}

void logData()
{
  String file_name = date_format;
  file_name += ".txt"; //file name: YYY-MM-DD.txt so all data collected in the same day, will be in the same log file
  LFile logFile = STORAGE.open(file_name.c_str(), FILE_WRITE);
  if (logFile)
  {
    message.printTo(logFile);
    logFile.write('\n');
    logFile.close();
  }
}

void readSensors()
{
  message["Temperature"] = random(5, 30);
  message["Humidity"] = random(10, 90);
}

void checkPlaces()
{
  getGPSData();
  if (true /*getGPSData()>3*/) //if we got atleast 3 satellites
  {
    for (int i = 0; i < data["places"].size(); ++i)
    {
      double aLat = -1, aLon = -1;
      String p = data["places"][i]["type"];
      if (strcmp(data["places"][i]["type"], "location") == 0)  //if the restriction is from a geografic point
      {
        aLat = data["places"][i]["lat"];
        aLon = data["places"][i]["lon"];
      }
      else //if the restriction is from another Sandro
      {
        c.flush();
        if (c.connect(pubnubServer, 80))
        {
          //c.setTimeout(2000);
          c.flush();
          c.print("GET /subscribe/");
          c.print(subKey);
          c.print("/");
          c.print((const char *)data["places"][i]["name"]);
          c.print("/0/");
          c.print(subTimetoken);
          c.println(" HTTP/1.1");
          c.println("Host: pubsub.pubnub.com");
          c.println("User-Agent: Linkit-ONE");
          c.println("Connection: close");
          c.println();
          if (c.find("\r\n\r\n")) //search for the body of the response
          {
            String response = "";
            while (c.available())response += (char)c.read();
            JsonArray& jResp = JSONBuffer.parseArray(response); 
            if (jResp.size() > 1)
            {
              JsonArray& msgs = jResp[0]; //A JSON array containing th messages post to the channel since last time we checcked
              if (msgs.size() > 0)
              {
                aLat = msgs[msgs.size() - 1]["lat"];
                aLon = msgs[msgs.size() - 1]["lon"];
              }
              subTimetoken = (const char*)jResp[1]; //Get the timetoken for the next subscribe request
            }
          }
          else //There aren't new messages on this channel
          {
            while (c.available())c.read(); // Not realy needed, but boh...just in case.
            subTimetoken = "0";
          }
          c.stop();
        }
      }
      if ((aLat != -1) && (aLon != -1))
      {
        double d = dist(aLat, aLon, latitude, longitude);
        if ((d > data["places"][i]["max"]) || (d < data["places"][i]["min"]))  //If we violated the distance restriction, note in in the device status
        {
          status += "Restriction ";
          status += i + 1;
          status += " violated: ";
          status += d;
          status += "km. ";
        }
      }
    }
  }
}


void sendData()
{
  message["status"] = status.equals("OK")?"allert" : status; //Al the other data are alredy in the message, so only the status is now left
  c.flush();
  if (c.connect(pubnubServer, 80))
  {
    c.flush();
    c.print("GET /publish/");
    c.print(pubKey);
    c.print("/");
    c.print(subKey);
    c.print("/0/");
    c.print(channel);
    c.print("/0/");
    String m = "";
    message.printTo(m); //I don't konw why, but message.printTo(c) don't always works
    c.print(m);
    c.println(" HTTP/1.1");
    c.println("Host: pubsub.pubnub.com");
    c.println("User-Agent: Linkit-ONE");
    c.println("Connection: close");
    c.println();
    if (c.find("\r\n\r\n")) //Search the body of the response
    {
      String response = "";
      while (c.available())response += (char)c.read();
      JsonArray& jResp = JSONBuffer.parseArray(response); //An array containing the esit of our publish request
      if (strcmp(jResp[1], "Sent") == 0)
      {
        //Serial.println("Message sent");
      }
    }
    else while (c.available())c.read();
    c.stop();
  }
}

boolean sendSMS(const char *num, String text)
{
  LSMS.beginSMS(num);
  LSMS.print(text);
  return LSMS.endSMS();
}


double dist(double lat1, double lon1, double lat2, double lon2)
{
  const double R = 6378.137;

  lat1 = PI * lat1 / 180;
  lat2 = PI * lat2 / 180;
  lon1 = PI * lon1 / 180;
  lon2 = PI * lon2 / 180;
  double d = R * acos(sin(lat2) * sin(lat1) + cos(lat2) * cos(lat1) * cos(fabs(lon1 - lon2)));
  return d;
}

void readSMS()
{
  String msg = "";
  String response = "";
  char number[20] = {'\0'};
  LSMS.remoteNumber(number, sizeof(number)); //Get the number of the sender
  while (LSMS.peek() > 0) msg += (char)LSMS.read(); //Get the content of the SMS
  msg.toLowerCase();
  if (msg.indexOf("where") > -1) //Maybe the sender wat to know our location
  {
    if (sat_num > 3) response += "I'm right here: "; //There are enought visible satellites, so the coordinates are attendible
    else response += "I'm not sure of my location, but before I was here: ";
    //Build a link to our position in Google Maps
    response += "http://www.google.com/maps?q=";
    response += lat_format;
    response += ",";
    response += lon_format;
    sendSMS(number, response);
    return;
  }
  for (int i = 0; i < data["sensors"].size(); ++i)  //Check if the message contains the name of one of the sensors
  {
    String lowerCase = (const char*) data["sensors"][i]["name"];
    lowerCase.toLowerCase();
    if (msg.indexOf(lowerCase) > -1)
    {
      response += (const char*) data["sensors"][i]["name"];
      response += " value is ";
      String name = data["sensors"][i]["name"]; //Sensor name
      int val = message[name]; //sensor value
      response += val;
      sendSMS(number, response);
      return;
    }
  }
  if (msg.indexOf("log") > -1) //Something about log
  {
    if ((msg.indexOf("start") > -1) || (msg.indexOf("begin") > -1) || (msg.indexOf("enable") > -1))
    {
      if (enable_log)response += "Log is already enabled";
      else
      {
        enable_log = true;
        response += "OK, I started logging";
      }
      sendSMS(number, response);
      return;
    }
    else if ((msg.indexOf("stop") > -1) || (msg.indexOf("end") > -1) || (msg.indexOf("disable") > -1))
    {
      if (!enable_log)response += "Log is already disabled";
      else
      {
        enable_log = false;
        response += "OK, I disabled logging";
      }
      sendSMS(number, response);
      return;
    }
    else
    {
      response += "Log is ";
      response += enable_log ? "enabled" : "disabled";
      sendSMS(number, response);
      return;
    }
  }
  response += "I don't understand wat you want, but here is everything I know: ";
  message.printTo(response);
  sendSMS(number, response);
  return;
}

byte getGPSData()
{
  gpsSentenceInfoStruct info;
  LGPS.getData(&info);
  if (info.GPGGA[0] == '$')
  {
    String str = (char*)(info.GPGGA);
    str = str.substring(str.indexOf(',') + 1);
    hour = str.substring(0, 2).toInt();
    minute = str.substring(2, 4).toInt();
    second = str.substring(4, 6).toInt();
    time_format = "";
    time_format += hour;
    time_format += ":";
    time_format += minute;
    time_format += ":";
    time_format += second;
    str = str.substring(str.indexOf(',') + 1);
    latitude = convert(str.substring(0, str.indexOf(',')), str.charAt(str.indexOf(',') + 1) == 'S');
    int val = latitude * 1000000;
    String s = String(val);
    lat_format = s.substring(0, (abs(latitude) < 100) ? 2 : 3);
    lat_format += '.';
    lat_format += s.substring((abs(latitude) < 100) ? 2 : 3);
    str = str.substring(str.indexOf(',') + 3);
    longitude = convert(str.substring(0, str.indexOf(',')), str.charAt(str.indexOf(',') + 1) == 'W');
    val = longitude * 1000000;
    s = String(val);
    lon_format = s.substring(0, (abs(longitude) < 100) ? 2 : 3);
    lon_format += '.';
    lon_format += s.substring((abs(longitude) < 100) ? 2 : 3);

    str = str.substring(str.indexOf(',') + 3);
    fix = str.charAt(0) - 48;
    str = str.substring(2);
    sat_num = str.substring(0, 2).toInt();
    str = str.substring(3);
    dop = str.substring(0, str.indexOf(',')).toFloat();
    str = str.substring(str.indexOf(',') + 1);
    altitude = str.substring(0, str.indexOf(',')).toFloat();
    str = str.substring(str.indexOf(',') + 3);
    geoid = str.substring(0, str.indexOf(',')).toFloat();

    if (info.GPRMC[0] == '$')
    {
      str = (char*)(info.GPRMC);
      int comma = 0;
      for (int i = 0; i < 60; ++i)
      {
        if (info.GPRMC[i] == ',')
        {
          comma++;
          if (comma == 7)
          {
            comma = i + 1;
            break;
          }
        }
      }
      str = str.substring(comma);
      kn_speed = str.substring(0, str.indexOf(',')).toFloat();
      kh_speed = kn_speed * 1.852;
      str = str.substring(str.indexOf(',') + 1);
      track_angle = str.substring(0, str.indexOf(',')).toFloat();
      str = str.substring(str.indexOf(',') + 1);
      day = str.substring(0, 2).toInt();
      month = str.substring(2, 4).toInt();
      year = str.substring(4, 6).toInt();
      date_format = "20";
      date_format += year;
      date_format += "-";
      date_format += month;
      date_format += "-";
      date_format += day;
      message["lat"] = lat_format;
      message["lon"] = lon_format;
      message["speed"] = kh_speed;
      message["sat_num"] = sat_num;
      return sat_num;
    }
  }
  return 0;
}


float convert(String str, boolean dir)
{
  double mm, dd;
  int point = str.indexOf('.');
  dd = str.substring(0, (point - 2)).toFloat();
  mm = str.substring(point - 2).toFloat() / 60.00;
  return (dir ? -1 : 1) * (dd + mm);
}

index.htlm

HTML
<!DOCTYPE html>
<html>
    <head>
        <title>Sandro</title>
        <link href="style.css" rel="stylesheet">
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
        <script src="Chart.js"></script>
        <script src="http://cdn.pubnub.com/pubnub-3.7.15.min.js"></script>
    </head>
    <body  style="background-color:#20351C;">
        <div id="top">
        <div style="margin: 0px 0px 0px 50px;">
            <h2>MY LOCATION</h2>
            <div style="margin: 10px 0px 0px 70px; display: inline-block;">
                <h3 style="display: inline-block;">Latitude</h3>
                <input  style="display: inline-block;" type="text" id="myLat" size="9" >
                <h3 style="margin: 0px 10px 0px 0px;display: inline-block;">Longitude</h3>
                <input type="text" id="myLon" size="9" >
                <button style="margin: 0px 20px 0px 30px;" onclick="updateLocation()">Update</button>
                <button onclick="getLocation()">Find me!</button>
            </div>
            <div style="margin: 0px 0px 0px 70px;">
                <h3 style="display: inline-block;" >Click n' set</h3>
                <div class="checkbox" style="border: 2px solid white;" >
  		            <input type="checkbox" id="clicknset" onclick="clickNSet()"/>
	  	            <label for="clicknset"></label>
	  	        </div>
	  	        <h3 style="display: inline-block;" >Show path</h3>
                <div class="checkbox" style="border: 2px solid white;" >
  		            <input type="checkbox" id="showpath" onclick="showPath()" checked="checked"/>
	  	            <label for="showpath"></label>
	  	        </div>
	  	        <button id="show_charts">Show charts</button>
  	        </div>
        </div>
        <hr>
	    <div style="margin: 0px 0px 0px 50px;" id="box"><select name="sel" id="sel" style="background-color: #B1FCE7; " onchange="selected()"></select><h3> <a>Latitude</a><input type="text" size="9" id="lat" readonly><a>Longitude</a><input type="text" size="9" id="lon" readonly><a>Distance</a><input type="text" size="9" id="dist" readonly><a>Speed</a><input type="text" size="9" id="speed" readonly></h3>
	    </div>
	    </div>
        <div id="map"></div>
        <div id="charts" style="width: 100%"></div>
        <script type="text/javascript">
            
            var trace = true;
            var map;
            var data; 
            var save; //Used to save data when we close the browser
       	    var myLocation; //Our coordinates
       	    var myMarker; //A marker on our location on the map
       		var my_uuid = PUBNUB.uuid();  //Needed only if we'll use pubnub presence API
            var charts = new Array();
       		var cDiv = document.getElementById("charts");
       		var mapHeight;
       		
            pubnub = PUBNUB
            ({                          
            	publish_key   : 'YOUR_PUBLISH_KEY',
        	    subscribe_key : 'YOUR_SUBSCRIBE_KEY',
        	    uuid: my_uuid
    	     });
    	    
    	    var saveFile = new XMLHttpRequest();
            saveFile.open("GET", "save.JSON", false); //Open file containing settings and previous data
    	    saveFile.onreadystatechange = function ()
   		    {
           		if(saveFile.readyState === 4)
           		{
           		    if(saveFile.status === 200 || saveFile.status == 0)
               		{
               		    var content = saveFile.responseText;
                        data = JSON.parse(content);
                        save = JSON.parse(content);
                        var channels = new Array();
                        var channelReg = 0;
                        for(var i=0; i <  data["sandros"].length; ++i) 
                        {
                            pubnub.subscribe(
                            {                                     
            				    channel : data["sandros"][i]["name"],
        				        message : newMessage,
        				        uuid: my_uuid, 
        				        connect: function()
        				        {
        				            channelReg++;
        				            if(channelReg == data["sandros"].length) //If we registered to all the channels
        				            {
        				                updateLabels();
        				                $( window ).resize(function(){});
        				                $("#show_charts").click(function()
        				                {
                                            if($(this).text() == "Show charts") 
                                            {
                                                $(this).html('Hide charts')
                                                $("#map").animate({height:'300'});
                                            }
                                            else 
                                            {
                                                $(this).html('Show charts');                                                
                                                $("#map").animate({height: mapHeight});
                                            }
                                        });
                                        setInterval(function () //Check if the devices ar still alive
                                        {
											var now = new Date();
											for(var j=0; j <  data["sandros"].length; ++j)
											{
											    var sDate = new Date(data["sandros"][j]["date_time"]);
												if((now.getTime()-sDate.getTime())> 120000)
												{
													data["sandros"][j]["marker"].setIcon('http://maps.google.com/mapfiles/ms/icons/red-dot.png');
												}
											}
										},5000);
        				            }
        				        }
        				        
    					    });
                            var s = document.getElementById("sel");
						    var option = document.createElement("option");
						    option.text = data["sandros"][i]["name"];
						    s.add(option);   
                        }
                        createCharts();
           		    }
   			    }
   			    window.onbeforeunload = closing;
   		    }
   		    saveFile.send(null);
   		    
   		    
            function newMessage(message, env, channel)
            {
        	    for(var i=0; i <  data["sandros"].length; ++i)
        	    {
            	    if(channel == data["sandros"][i]["name"])
        	        {
            	        data["sandros"][i]["lat"] = message["lat"];
            	        data["sandros"][i]["status"] = message["status"];
        	            data["sandros"][i]["lon"] = message["lon"];
        	            data["sandros"][i]["speed"] = message["speed"];
        	            data["sandros"][i]["date_time"] = new Date();
        	            data["sandros"][i]["marker"].setPosition(new google.maps.LatLng(message["lat"],message["lon"]));
        	            data["sandros"][i]["line"].setPath([myLocation,data["sandros"][i]["marker"].getPosition()]);
        	            data["sandros"][i]["date_time"] = new Date();
        	            if(trace)data["sandros"][i]["path"].getPath().push(new google.maps.LatLng(message["lat"],message["lon"]));
                        var sel = document.getElementById("sel");
                        if(channel == sel.options[sel.selectedIndex].value)
                        {
                            if(data["sandros"][i].hasOwnProperty("sensors"))
                            {
            	                for(k = 0; k < data["sandros"][i]["sensors"].length;++k)
        	                    {
            	                    if(message.hasOwnProperty(data["sandros"][i]["sensors"][k]["name"]))
        	                        {
            	                        var d = new Date();
        	                            var time = ""+d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds();
                                        charts[k].chart.addData([message[data["sandros"][i]["sensors"][k]["name"]]], time);
                                        if(charts[k].datanum < 6)charts[k].datanum++;
                                        else charts[k].chart.removeData( );
        	                        }   
            	                }
                            }
        	            }
        	            if(message["status"] == "OK")data["sandros"][i]["marker"].setAnimation(null);
        	            else data["sandros"][i]["marker"].setAnimation(google.maps.Animation.BOUNCE); //there is an alert
        	            if(message["sat_num"] < 3)data["sandros"][i]["marker"].setIcon('http://maps.google.com/mapfiles/ms/icons/yellow-dot.png');
        	            else data["sandros"][i]["marker"].setIcon('http://maps.google.com/mapfiles/ms/icons/green-dot.png');
        	            updateLabels();
        	        }
        	    }
            }   		    
    	    
            function closing()
            {
                for(var i=0; i <  data["sandros"].length; ++i) 
                {
                    pubnub.unsubscribe({channel: data["sandros"][i]["name"],}); //Unsubscribe to each channels
                }
                var s = saveData(); //save data before closing the page
                if(s == "OK")return null;
                return "Error saving data";
            }

		    function initMap() 
		    {
		        var wHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; //Get the windows height
		        mapHeight = "" + (wHeight - document.getElementById("top").clientHeight) + "px"; //Make the map fill the space left in th ebottom of the screen
		        document.getElementById("map").style.height = mapHeight;

    			map = new google.maps.Map(document.getElementById('map'), 
    		    {
        			center: {lat: -34.397, lng: 150.644}, //Australia
    			    zoom: 10
  			    });
                
                myMarker = new google.maps.Marker(
                {
                    position: myLocation,
                    icon: 'http://maps.google.com/mapfiles/ms/icons/blue-dot.png',
                    map: map,
                    color: 'blue',
                    title: 'Me'
                });
                
                for(var i=0; i <  data["sandros"].length; ++i)
                {
                    var marker = new google.maps.Marker(
                    {
                        position: new google.maps.LatLng(data["sandros"][i]["lat"], data["sandros"][i]["lon"]),
                        map: map,
                        animation: google.maps.Animation.DROP,
                        icon: 'http://maps.google.com/mapfiles/ms/icons/red-dot.png',
                        title: data["sandros"][i]["name"]
                    });
                    
                    data["sandros"][i]["marker"] = marker;
                    
                    data["sandros"][i]["line"] = new google.maps.Polyline( //A red line that connects me to sandro on the map
         		    {
                        geodesic: true,
                        strokeColor: '#FF0000',
                        strokeOpacity: 1.0,
                        strokeWeight: 2,
                        map: map,
                    });
                    
                    data["sandros"][i]["path"] = new google.maps.Polyline(
                    {
                        path: [],
                        geodesic: true,
                        strokeColor: getRandomColor(),
                        strokeOpacity: 1.0,
                        strokeWeight: 2,
                        map: map,
                    });
                    
                    var info_window = new google.maps.InfoWindow(
                    {
                        content: "info"
                    });
                    
                    data["sandros"][i]["info_window"] = info_window;
                    
                    data["sandros"][i]["marker"].addListener('click', clickEvent );
                    
                }
                getLocation();
		    }  
		    
		    
		    
		    function clickEvent() //when I click on a marker
		    {
		        
		        var content = '<img src="http://maps.googleapis.com/maps/api/streetview?size=400x400&location='+this.position.lat()+','+this.position.lat()+'&fov=90&heading=235&pitch=10&key=YOUR_MAPS_KEY">';
		        var info_window = new google.maps.InfoWindow({content: content});   
		        info_window.open(map, this);
		    }
		    
		    
		    function getRandomColor() //create random colors for the paths
		    {
                var hexLetters = '0123456789ABCDEF'.split('');
                var color = '#';
                for (var i = 0; i < 6; i++ ) 
                {
                    color += hexLetters[Math.floor(Math.random() * 16)];
                }
                return color;
            }

            function getLocation()
            {
                if(navigator.geolocation) 
                {
                    var siberia = new google.maps.LatLng(60, 105);
		    	    var newyork = new google.maps.LatLng(40.69847032728747, -73.9514422416687);
		    	    if(data.hasOwnProperty("my_last_location"))
                    {
                        if(data["my_last_location"].length > 1)
                        {
                            myLocation = new google.maps.LatLng(data["my_last_location"][0],data["my_last_location"][1]);
                        }
                    }
    			    navigator.geolocation.getCurrentPosition(function(position) 
                    {
          				myLocation = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
      				    setLocation();
    			    }, 
                    function() 
            	    {
      				    if(myLocation == undefined )
      				    {
      				        myLocation = newyork;
      				        alert("Geolocation service failed. Don, are you trying to connect from New York's subway again?");
      				    }
      				    else alert("Geolocation service failed. I will set your last location");
      				    setLocation();
    			    });
  			    }
			    else 
                {
      		    	if(myLocation == undefined )
      		    	{
      		    	    myLocation = siberia;
      		    	    alert("Your browser doesn't support geolocation. I hope its because you live in Siberia...");
      		    	}
      		    	else alert("Your browser doesn't support geolocation. I will set your last location"); 
      		    	setLocation();
                } 
            }	

            function setLocation()
            {
                document.getElementById("myLat").value = myLocation.lat().toFixed(6);
                document.getElementById("myLon").value = myLocation.lng().toFixed(6);
                myMarker.setPosition(myLocation);
                map.setCenter(myLocation);
                for(var i=0; i <  data["sandros"].length; ++i)
                {
                    data["sandros"][i]["line"].setPath([myLocation, data["sandros"][i]["marker"].getPosition()]);
                }
            } 

            function updateLocation()
            {   
                myLocation = new google.maps.LatLng(document.getElementById('myLat').value,document.getElementById('myLon'   ).value);
                setLocation();
            }            

	        function clickNSet()
	        {
	            if(document.getElementById("clicknset").checked)
	            {
	                google.maps.event.addListener(map, 'click', function(event) 
                    {
                        myLocation = event.latLng;
                        setLocation();
                        updateLabels();
                    });
	            }
	            else google.maps.event.clearListeners(map, 'click');
	        }            

            function updateLabels()
            {
                var index = document.getElementById("sel").selectedIndex;
                document.getElementById("lat").value = data["sandros"][index]["lat"];
                document.getElementById("lon").value = data["sandros"][index]["lon"];
                document.getElementById("dist").value = (google.maps.geometry.spherical.computeDistanceBetween (new google.maps.LatLng(data["sandros"][index]["lat"],data["sandros"][index]["lon"]), myLocation)/1000).toFixed(2);
                document.getElementById("speed").value = data["sandros"][index]["speed"];
            }
            
            function selected()
            {
                updateLabels();
                createCharts();
            }
            
            function saveData()
            {
                for(var i=0; i <  data["sandros"].length; ++i) 
                {
                    save["sandros"][i]["lat"] = data["sandros"][i]["lat"];
                    save["sandros"][i]["lon"] = data["sandros"][i]["lon"];
                    save["sandros"][i]["speed"] = data["sandros"][i]["speed"];
                    save["sandros"][i]["date_time"] = data["sandros"][i]["date_time"];
                }
                save["my_last_location"][0] = myLocation.lat();
                save["my_last_location"][1] = myLocation.lng();
                var url = "save_data.php";
                var params = "data="+JSON.stringify(save);
                var http = new XMLHttpRequest();
                var waitResponse = true;
                var response = "response text";
                http.open("POST", url, false);
                http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
                http.send(params);
                return http.responseText;
            }
            function showPath()
            {
                trace = !trace;
                for(var i=0; i <  data["sandros"].length; ++i) 
                {
                    data["sandros"][i]["path"].setPath([]);
                }
            }
            function createCharts()
            {
                var index = document.getElementById("sel").selectedIndex;
                charts = [];
                while (cDiv.firstChild) cDiv.removeChild(cDiv.firstChild);
                if(data["sandros"][index].hasOwnProperty("sensors"))
                {
                    for(var i=0; i < data["sandros"][index]["sensors"].length;++i)
                    {
                        var title = document.createElement("h2");
                        title.innerHTML = data["sandros"][index]["sensors"][i]["name"];
                        cDiv.appendChild(title);
                    
                        var canvas = document.createElement("canvas");
                        canvas.id = data["sandros"][index]["sensors"][i]["name"];
                        canvas.style.width = "100%";
                        canvas.style.height = "200px";
                        cDiv.appendChild(canvas);
                        cDiv.appendChild(document.createElement("hr"));
                        chartStyle = 
                        {
                            labels: [],
                            datasets: 
                            [
                                {
                                    fillColor: "rgba(255,255,255,0.2)",
                                    strokeColor: "rgba(255,255,255,1)",
                                    pointColor: "rgba(255,255,255,1)",
                                    pointStrokeColor: "#fff",
                                    data: []
                                }
                            ]
                        };
                        charts.push({canvas: canvas, chart: new Chart(canvas.getContext('2d') ).Line(chartStyle, {animationSteps: 15,  scaleFontColor: "#ffffff" }), datanum: 0});
                        charts[i].chart.fillColor =  "rgba(55,25,5,1)";
                        charts[i].chart.update();
                    }
                }
            }
        </script>
        <script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_MAPS_KEY&v=3&libraries=geometry&callback=initMap">
        </script>
    </body>
</html>

save_data.php

PHP
<?php
    //GEts POST parameters
    function getParameter($par, $default = null)
	{
            if (isset($_POST[$par]) && strlen($_POST[$par])) return $_POST[$par];
            if (isset($_GET[$par]) && strlen($_GET[$par])) return $_GET[$par];
            else return $default;
    }
    $data = getParameter("data");
    if($data != null)
    {
        file_put_contents("save.JSON", json_encode(json_decode($data), JSON_PRETTY_PRINT), LOCK_EX); //Decode JSON and save data to file
        echo "OK";    
    }
    else echo "error";
?>

style.css

CSS
html, body { height: 100%; margin: 0; padding: 0; }
#sel { height: 25px; width: 100px; }
h2, h3 {color:white;}
input 
{
    border: 5px solid white; 
    -webkit-box-shadow: 
      inset 0 0 8px  rgba(0,0,0,0.1),
            0 0 16px rgba(0,0,0,0.1); 
    -moz-box-shadow: 
      inset 0 0 8px  rgba(0,0,0,0.1),
            0 0 16px rgba(0,0,0,0.1); 
    box-shadow: 
      inset 0 0 8px  rgba(0,0,0,0.1),
            0 0 16px rgba(0,0,0,0.1); 
    padding: 2px;
    background: rgba(19,91,84,1);
    margin: 0px 20px 0px 10px;
}

button 
{
	-moz-box-shadow:inset 0px 1px 3px 0px #91b8b3;
	-webkit-box-shadow:inset 0px 1px 3px 0px #91b8b3;
	box-shadow:inset 0px 1px 3px 0px #91b8b3;
	background-color:#135B54;
	-moz-border-radius:5px;
	-webkit-border-radius:5px;
	border-radius:5px;
	border:1px solid #566963;
	display:inline-block;
	cursor:pointer;
	color:#ffffff;
	font-family:Arial;
	font-size:15px;
	font-weight:bold;
	padding:6px 23px;
	text-decoration:none;
	text-shadow:0px -1px 0px #2b665e;
	margin: 0px 20px 0px 10px;
}

button:hover 
{
	background-color:#768d87;
}

button:active 
{
	position:relative;
	top:1px;
}



input[type=checkbox] 
{
	visibility: hidden;
}


.checkbox 
{
	width: 120px;
	height: 40px;
	background: #333;
	vertical-align: middle;
    display: inline-block;
	border-radius: 50px;
	margin: 0px 20px;
	position: relative;
}


.checkbox:before 
{
	content: 'On';
	position: absolute;
	top: 12px;
	left: 13px;
	height: 2px;
	color: #26ca28;

	font-size: 16px;
}


.checkbox:after 
{
	content: 'Off';
	position: absolute;
	top: 12px;
	left: 84px;
	height: 2px;
	color: white;
	font-size: 16px;
}



.checkbox label 
{
	display: block;
	width: 52px;
	height: 22px;
	border-radius: 50px;

	-webkit-transition: all .5s ease;
	-moz-transition: all .5s ease;
	-o-transition: all .5s ease;
	-ms-transition: all .5s ease;
	transition: all .5s ease;
	cursor: pointer;
	position: absolute;
	top: 9px;
	z-index: 1;
	left: 12px;
	background: #ddd;
}


.checkbox input[type=checkbox]:checked + label 
{
	left: 60px;
	background: #26ca28;
}

save.JSON

JSON
{
    "my_last_location": [
        45.4090043,
        11.8896656
    ],
    "sandros": [
        {
            "name": "Sandro",
            "lat": "45.424945",
            "date_time": "2015-11-29T16:59:21.461Z",
            "sensors": [
                {
                    "name": "Temperature",
                    "min": 6,
                    "max": 50
                },
                {
                    "name": "Humidity",
                    "min": 10,
                    "max": 80
                }
            ],
            "lon": "11.876460",
            "speed": 0
        },
        {
            "name": "Cisca",
            "lat": "45.425270",
            "lon": "11.877168",
            "date_time": "2015-11-28T10:26:28.161Z",
            "speed": 0
        },
        {
            "name": "Faggio",
            "lat": "45.800135",
            "lon": "11.800747",
            "date_time": "2015-11-27T16:14:50.569Z",
            "speed": "10.64"
        }
    ]
}

Credits

Mohamed Fadiga

Mohamed Fadiga

8 projects • 28 followers
Teaher/ Maker / developer / IoT addicted / Sprinter 📚💡🤓💻. Passionate about anime, manga and pop-punk music. 🍥📖🎬🎸

Comments

Add projectSign up / Login