GPS Distance between two points

Hi all

I am attempting to make a GPS device that returns "distance traveled". I am using the itead GPS shield and arduino UNO. So far, I have the GPS working, but I am having trouble calculating the distance between different latitude and longitude.

Attached are two codes, the first is the sketch that is working and the second is the code I found online, which is suppose to calculate the distance traveled. I have made an attempt to merge the two together but it will not compile. Could someone help me figure this out or guide me in the right direction.

Thanks

#include <TinyGPS++.h>
#include <SoftwareSerial.h>

int RXPin = 2; //conntec to tx pin of the gps
int TXPin = 3; //conntect to RX pin of the gps

// The TinyGPS++ object
TinyGPSPlus gps;

// The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);

void setup()
{
  Serial.begin(115200);
  ss.begin(9600);

  Serial.println();
  Serial.println(F("Sats    Latitude    Longitude      Date      Time    Altitude    Speed"));
  Serial.println(F("         (deg)        (deg)                  (UTC)     (m)       (mph)"));
  Serial.println(F("________________________________________________________________________"));
}

void loop()
{
  
  printInt(gps.satellites.value(), gps.satellites.isValid(), 9); 
  printFloat(gps.location.lat(), gps.location.isValid(), 12, 3); 
  printFloat(gps.location.lng(), gps.location.isValid(), 12, 3); 
  printDateTime(gps.date, gps.time);
  printFloat(gps.altitude.meters(), gps.altitude.isValid(), 12, 2);
  printFloat(gps.speed.mph(), gps.speed.isValid(), 6, 2);
  
  Serial.println();
  
  smartDelay(1000);

  if (millis() > 5000 && gps.charsProcessed() < 10)
    Serial.println(F("No GPS data received: check wiring"));
}

// This custom version of delay() ensures that the gps object
// is being "fed".
static void smartDelay(unsigned long ms)
{
  unsigned long start = millis();
  do 
  {
    while (ss.available())
      gps.encode(ss.read());
  } while (millis() - start < ms);
}

static void printFloat(float val, bool valid, int len, int prec)
{
  if (!valid)
  {
    while (len-- > 1)
      Serial.print('*');
    Serial.print(' ');
  }
  else
  {
    Serial.print(val, prec);
    int vi = abs((int)val);
    int flen = prec + (val < 0.0 ? 2 : 1); // . and -
    flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
    for (int i=flen; i<len; ++i)
      Serial.print(' ');
  }
  smartDelay(0);
}

static void printInt(unsigned long val, bool valid, int len)
{
  char sz[32] = "*****************";
  if (valid)
    sprintf(sz, "%ld", val);
  sz[len] = 0;
  for (int i=strlen(sz); i<len; ++i)
    sz[i] = ' ';
  if (len > 0) 
    sz[len-1] = ' ';
  Serial.print(sz);
  smartDelay(0);
}

static void printDateTime(TinyGPSDate &d, TinyGPSTime &t)
{
  if (!d.isValid())
  {
    Serial.print(F("********** "));
  }
  else
  {
    char sz[32];
    sprintf(sz, "%02d/%02d/%02d ", d.month(), d.day(), d.year());
    Serial.print(sz);
  }
  
  if (!t.isValid())
  {
    Serial.print(F("******** "));
  }
  else
  {
    char sz[32];
    sprintf(sz, "%02d:%02d:%02d ", t.hour(), t.minute(), t.second());
    Serial.print(sz);
  }
  
  smartDelay(0);
}

static void printStr(const char *str, int len)
{
  int slen = strlen(str);
  for (int i=0; i<len; ++i)
    Serial.print(i<slen ? str[i] : ' ');
  smartDelay(0);
}
// get distance example
// using TinyGPS
// Written by Bill Heaster


#include <TinyGPS.h> //include for the GPS handeling.

// These would be saved in the EEPROM for your GeoCache
// For this example we will name them as const variables
const long TargetLat = 43164622;
const long TargetLon = 77609596;

TinyGPS gps;



struct //structure to organize our variables.
{
    float miles;
    float feet;
    float km;
}distance;

void setup()
{
    Serial.begin(115200); //start serial for the GPS module
}

void loop()
{
    if(isData())
    {
        getDistance(gps,TargetLat, TargetLon);
    }
    Serial.print("Miles: ");
    Serial.println(distance.miles);

    Serial.print("KM: ");
    Serial.println(distance.km);

    Serial.print("Feet: ");
    Serial.println(distance.feet);
}

bool isData() // feed the gps object with serial info and check if we are recieving a valid packet
{
  while (Serial.available())
  {
    if (gps.encode(Serial.read()))
      return true;
  }
  return false;
}

void getDistance(TinyGPS &gps, long targetLat, long targetLon)
{
  long lat, lon;
  unsigned long age;
  float latRad, lonRad;
  float flat, flon;
  float tlat, tlon;
  float tlatRad, tlonRad;
  float midLat, midLon;
  int samples=0;
  //----------------------------------------

  for (samples; samples< 20; samples++) //collect a few samples to make the data more accurate.Not sure if this is the best soloution.
  {
    gps.get_position(&lat, &lon, &age); //get the coords from the tinygps object
    if(age >= 5000) //if the data is old
    {
      delay(3000);//wait before we make another attempt
      isData(); //refresh the GPS object.
    }
    else
    {
        samples = 20;
    }
  }

  //convert to decimal degree
  flat = lat /100000.0;
  flon = lon /100000.0;
  tlat = targetLat / 100000.0;
  tlon = targetLon / 100000.0;

  //convert decimal degree into radian
  latRad = flat * 0.017453293;
  lonRad = flon * 0.017453293;
  tlatRad = tlat * 0.017453293;
  tlonRad = tlon * 0.017453293;

  midLat = tlatRad - latRad;
  midLon = tlonRad - lonRad;

  //Calculate the distance in KM
  float latSin = sin((latRad - tlatRad)/2);
  float lonSin = sin((lonRad - tlonRad)/2);
  distance.km = 2 * asin(sqrt((latSin*latSin) + cos(latRad) * cos(tlatRad) * (lonSin * lonSin)));
  distance.km = distance.km * 6371;



  //convert to miles
  distance.miles = distance.km * 0.621371192;
  distance.feet = distance.miles *5280;
  isData(); //refresh GPS during long calculations

  // --------------------------------

  }

but it will not compile.

And we have to guess what the compilation errors are?

And we have to guess what the compilation errors are?

As well as how you mangled the two versions into one?

Well I kinda want to know if it is possible to combine the two, the second sketch was made to be used with some geo catch device but since it uses the TinyGPS library I think it should be possible thus why I want to try and combine the two sketches. I will try to do this now and see what happens.

Also could you guys suggest a method of combining the two, that's where I am having the most trouble.

Thanks

So attached is a version of the code I have combined. Without the "get distance" part, the sketch complies and prints data on the LCD but once I add the "get distance" part, I get the following error:

sketch_nov26c.ino: In function 'void loop()':
sketch_nov26c:106: error: 'TargetLat' was not declared in this scope
sketch_nov26c:106: error: 'TargetLon' was not declared in this scope
sketch_nov26c.ino: In function 'bool isData()':
sketch_nov26c:197: error: 'class LiquidCrystal_I2C' has no member named 'available'
sketch_nov26c:199: error: 'class LiquidCrystal_I2C' has no member named 'read'

Could someone help me fix this please.

// GPS Setup
#include "TinyGPS.h"
#include <SoftwareSerial.h>

// LCD Setup
#include <LiquidCrystal_I2C.h>
#include "Wire.h"

// GPS pin connections
int RXPin = 2; //conntec to tx pin of the gps
int TXPin = 3; //conntect to RX pin of the gps

// The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);

// GPS object
TinyGPS gps;

// LCD object
LiquidCrystal_I2C
  lcd(0x3F,20,4);

// Constants 
static void smartdelay(unsigned long ms);
static void print_float(float val, float invalid, int len, int prec);
static void print_int(unsigned long val, unsigned long invalid, int len);
static void print_date(TinyGPS &gps);
static void print_str(const char *str, int len);

// Variables
struct 
{
    float miles;
    float feet;
    float km;
}distance;

void setup()
{

  ss.begin(9600);
  lcd.init();
  lcd.backlight();
  lcd.setCursor(6,0);
  lcd.print("Welcome");
  delay(60000);
  
}

void loop()
{
  float flat, flon;
  unsigned long age, date, time, chars = 0;
  unsigned short sentences = 0, failed = 0;
  
  lcd.clear();
  
  lcd.setCursor(0,0);
  lcd.print("Sats in view:");
  print_int(gps.satellites(), TinyGPS::GPS_INVALID_SATELLITES,15);
  
  lcd.setCursor(0,1);
  lcd.print("It is (UTC)");
  
  lcd.setCursor(0,2);
  print_date(gps);
  
  delay(5000);
  lcd.clear();
  
  lcd.setCursor(0,0);
  lcd.print("Position (deg)");
  
  gps.f_get_position(&flat, &flon, &age);
  
  lcd.setCursor(0,1);
  lcd.print("Lat:");
  print_float(flat, TinyGPS::GPS_INVALID_F_ANGLE, 10, 3);
  
  lcd.setCursor(0,2);
  lcd.print("Long:");
  print_float(flon, TinyGPS::GPS_INVALID_F_ANGLE, 11, 3);
  
  delay(5000);
  lcd.clear();
  
  lcd.setCursor(0,0);
  lcd.print("Altitude (m)");
  lcd.setCursor(0,1);
  print_float(gps.f_altitude(), TinyGPS::GPS_INVALID_F_ALTITUDE, 7, 2);
  
  lcd.setCursor(0,2);
  lcd.print("Current Speed (mph)");
  lcd.setCursor(0,3);
  print_float(gps.f_speed_mph(), TinyGPS::GPS_INVALID_F_SPEED, 6, 2);
  
  delay(5000);
  lcd.clear();
  
  lcd.setCursor(0,0);
  lcd.print("Distance");
  lcd.setCursor(0,1);
  
   if(isData())
    {
        getDistance(gps,TargetLat, TargetLon);
    }
    //Serial.print("Miles: ");
    lcd.println(distance.miles);

    //Serial.print("KM: ");
    //Serial.println(distance.km);

    //Serial.print("Feet: ");
    //Serial.println(distance.feet);
  
  delay(5000);
  lcd.clear();
  
  smartdelay(1000);
}

static void smartdelay(unsigned long ms)
{
  unsigned long start = millis();
  do 
  {
    while (ss.available())
      gps.encode(ss.read());
  } while (millis() - start < ms);
}

static void print_float(float val, float invalid, int len, int prec)
{
  if (val == invalid)
  {
    while (len-- > 1)
      lcd.print('*');
    lcd.print(' ');
  }
  else
  {
    lcd.print(val, prec);
    int vi = abs((int)val);
    int flen = prec + (val < 0.0 ? 2 : 1); // . and -
    flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
    for (int i=flen; i<len; ++i)
      lcd.print(' ');
  }
  smartdelay(0);
}

static void print_int(unsigned long val, unsigned long invalid, int len)
{
  char sz[32];
  if (val == invalid)
    strcpy(sz, "*******");
  else
    sprintf(sz, "%ld", val);
  sz[len] = 0;
  for (int i=strlen(sz); i<len; ++i)
    sz[i] = ' ';
  if (len > 0) 
    sz[len-1] = ' ';
  lcd.print(sz);
  smartdelay(0);
}

static void print_date(TinyGPS &gps)
{
  int year;
  byte month, day, hour, minute, second, hundredths;
  unsigned long age;
  gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age);
  if (age == TinyGPS::GPS_INVALID_AGE)
   lcd.print("********** ******** ");
  else
  {
    char sz[32];
    sprintf(sz, "%02d/%02d/%02d %02d:%02d:%02d ",
        month, day, year, hour, minute, second);
    lcd.print(sz);
  }
  smartdelay(0);
}

static void print_str(const char *str, int len)
{
  int slen = strlen(str);
  for (int i=0; i<len; ++i)
    lcd.print(i<slen ? str[i] : ' ');
  smartdelay(0);
}

bool isData() // feed the gps object with serial info and check if we are recieving a valid packet
{
  while (lcd.available())
  {
    if (gps.encode(lcd.read()))
      return true;
  }
  return false;
}

void getDistance(TinyGPS &gps, long targetLat, long targetLon)
{
  long lat, lon;
  unsigned long age;
  float latRad, lonRad;
  float flat, flon;
  float tlat, tlon;
  float tlatRad, tlonRad;
  float midLat, midLon;
  int samples=0;
  //----------------------------------------

  for (samples; samples< 20; samples++) //collect a few samples to make the data more accurate.Not sure if this is the best soloution.
  {
    gps.get_position(&lat, &lon, &age); //get the coords from the tinygps object
    if(age >= 5000) //if the data is old
    {
      delay(3000);//wait before we make another attempt
      isData(); //refresh the GPS object.
    }
    else
    {
        samples = 20;
    }
  }

  //convert to decimal degree
  flat = lat /100000.0;
  flon = lon /100000.0;
  tlat = targetLat / 100000.0;
  tlon = targetLon / 100000.0;

  //convert decimal degree into radian
  latRad = flat * 0.017453293;
  lonRad = flon * 0.017453293;
  tlatRad = tlat * 0.017453293;
  tlonRad = tlon * 0.017453293;

  midLat = tlatRad - latRad;
  midLon = tlonRad - lonRad;

  //Calculate the distance in KM
  float latSin = sin((latRad - tlatRad)/2);
  float lonSin = sin((lonRad - tlonRad)/2);
  distance.km = 2 * asin(sqrt((latSin*latSin) + cos(latRad) * cos(tlatRad) * (lonSin * lonSin)));
  distance.km = distance.km * 6371;



  //convert to miles
  distance.miles = distance.km * 0.621371192;
  distance.feet = distance.miles *5280;
  isData(); //refresh GPS during long calculations

  // --------------------------------

  }

sketch_nov26c:197: error: 'class LiquidCrystal_I2C' has no member named 'available'
sketch_nov26c:199: error: 'class LiquidCrystal_I2C' has no member named 'read'

you cannot read from a display,
probably replace that with Serial.available()/read()

So I have changed what I was doing and simply decided to use the "distanceBetween()" function provided by the TinyGPS++ library. My problem now is determining how to save/make constant the INITIAL latitude and longitude because from the following code you can see that calculating the distance between current latitude and longitude and some constant latitude and longitude is quite simple.

  static const double start_Lat = 38.5539 , start_Lon = -121.7381 ; 
  
  // In KM
  unsigned long distanceKmTo =
    (unsigned long)TinyGPSPlus::distanceBetween(
      gps.location.lat(),
      gps.location.lng(),
      start_Lat, 
      start_Lon) / 1000;
  printInt(distanceKmTo, gps.location.isValid(), 8);
  
  // In miles
  unsigned long distanceMiTo=
    distanceKmTo*0.621371192;
    printInt(distanceMiTo, gps.location.isValid(), 9);

I would like to have "start_Lat" and start_Lon" to be the actual start latitude and longitude when I turn on the device. I hope I am communicating my goal correctly. If I can do this, I will be able to calculate the distance in real time as I walk, bike, drive ect.

I think this method is more simple except for this part that I am stuck on.

Thanks in advance

The entire sketch looks like this:

#include <TinyGPS++.h>
#include <SoftwareSerial.h>

int RXPin = 2; //conntec to tx pin of the gps
int TXPin = 3; //conntect to RX pin of the gps

// The TinyGPS++ object
TinyGPSPlus gps;

// The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);

void setup()
{
  Serial.begin(115200);
  ss.begin(9600);

  Serial.println();
  Serial.println(F("Sats    Latitude    Longitude      Date      Time    Altitude    Speed         Distance"));
  Serial.println(F("         (deg)        (deg)                  (UTC)     (m)       (mph)      (km)    (mi)"));
  Serial.println(F("________________________________________________________________________"));
}

void loop()
{
  
  printInt(gps.satellites.value(), gps.satellites.isValid(), 9); 
  printFloat(gps.location.lat(), gps.location.isValid(), 12, 3); 
  printFloat(gps.location.lng(), gps.location.isValid(), 12, 3); 
  printDateTime(gps.date, gps.time);
  printFloat(gps.altitude.meters(), gps.altitude.isValid(), 12, 2);
  printFloat(gps.speed.mph(), gps.speed.isValid(), 12, 2);
  
  static const double start_Lat = 38.5539 , start_Lon = -121.7381 ; 
  
  // In KM
  unsigned long distanceKmTo =
    (unsigned long)TinyGPSPlus::distanceBetween(
      gps.location.lat(),
      gps.location.lng(),
      start_Lat, 
      start_on) / 1000;
  printInt(distanceKmTo, gps.location.isValid(), 8);
  
  // In miles
  unsigned long distanceMiTo=
    distanceKmTo*0.621371192;
    printInt(distanceMiTo, gps.location.isValid(), 9);  

  Serial.println();
  
  smartDelay(1000);

  if (millis() > 5000 && gps.charsProcessed() < 10)
    Serial.println(F("No GPS data received: check wiring"));
}

// This custom version of delay() ensures that the gps object
// is being "fed".
static void smartDelay(unsigned long ms)
{
  unsigned long start = millis();
  do 
  {
    while (ss.available())
      gps.encode(ss.read());
  } while (millis() - start < ms);
}

static void printFloat(float val, bool valid, int len, int prec)
{
  if (!valid)
  {
    while (len-- > 1)
      Serial.print('*');
    Serial.print(' ');
  }
  else
  {
    Serial.print(val, prec);
    int vi = abs((int)val);
    int flen = prec + (val < 0.0 ? 2 : 1); // . and -
    flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
    for (int i=flen; i<len; ++i)
      Serial.print(' ');
  }
  smartDelay(0);
}

static void printInt(unsigned long val, bool valid, int len)
{
  char sz[32] = "*****************";
  if (valid)
    sprintf(sz, "%ld", val);
  sz[len] = 0;
  for (int i=strlen(sz); i<len; ++i)
    sz[i] = ' ';
  if (len > 0) 
    sz[len-1] = ' ';
  Serial.print(sz);
  smartDelay(0);
}

static void printDateTime(TinyGPSDate &d, TinyGPSTime &t)
{
  if (!d.isValid())
  {
    Serial.print(F("********** "));
  }
  else
  {
    char sz[32];
    sprintf(sz, "%02d/%02d/%02d ", d.month(), d.day(), d.year());
    Serial.print(sz);
  }
  
  if (!t.isValid())
  {
    Serial.print(F("******** "));
  }
  else
  {
    char sz[32];
    sprintf(sz, "%02d:%02d:%02d ", t.hour(), t.minute(), t.second());
    Serial.print(sz);
  }
  
  smartDelay(0);
}

static void printStr(const char *str, int len)
{
  int slen = strlen(str);
  for (int i=0; i<len; ++i)
    Serial.print(i<slen ? str[i] : ' ');
  smartDelay(0);
}