Really Remote Start Project


#1

I have a 2008 Chevy with factory remote start that I drive to a commuter lot every day where I catch a bus downtown. It gets really hot here in the summer in Texas and I always wanted to be able to start my car about 5 minutes before I get back to the commuter lot. Unfortunately, the key fob remote does not reach that far when I am five minutes out. I know the 2011 and newer GM cars have an OnStar app that would let me do what I want but that by itself isn’t a good reason to buy a new car.

I started doing some Arduino projects late last year and stumbled upon a few articles about GSM relays and then found this on reddit:

After reading that, I ordered an extra key fob transmitter for my car. I took it apart and traced all the button contacts. Once I figured out which ones did what I soldered wires to the v+ side of the contacts. Then I connected wires from the battery holder +/- to the arduino 3.3v and ground. One thing about Ben’s project that gave me pause was that he wired his spare remote directly up to the Arduino pins. I knew a relay would provide better protection but it seemed like overkill for a 3 volt transmitter. I did some research and decided that the best altermative for this project was to use NPN transistors as switches. When set HIGH by the arduino, the transistors “push the buttons” for me. I wrote a simple sketch that used input from the serial monitor in place of text messages to test out the transistors and it worked great. I had no problem working out the timing to trigger the remote buttons this way. Then I started looking at cellular module options. Once I saw the Adafruit Feather Fona (2G), I knew an all in one microcontroller cellular module was what I wanted. So now I just needed to decide which one to get and I soon discovered that most of the modules out there were 2G. I wanted something futureproof and a 2G module wasn’t going to cut it. It was then that I found and ordered a Hologram Dash. It took me a little while to figure out the API but I now have a working prototype that can do everything I can do with the remote but at a distance using texts and the Hologram cloud. I also added a BME280 i2c temperature sensor so I can know how hot or cold it is in my car. Finally, I kicked it up a notch by adding a GPS so I can locate my car, if needed.

Here are the available text commands:

Status - sends a reply with temperature, humidity, cellular signal strength and carrier network
Start - remote starts the car
Abort - cancels the remote start (don’t use Cancel as a command or it will unsubscribe you from texts)
Lock - locks the doors
Unlock - unlocks the doors
Horn - briefly honks the horn
Locate - honks the horn for a long time so you can find your car
Find - powers up the GPS module with a predifined timeout, waits for a lat/lon fix and then sends you a google maps link with a location pin followed by a more detailed text with lat/lon, speed, heading, HDOP and number of sats. The timeout is reset each time you send a Find command.
GPSOFF - turns the GPS module off
GPSON - turns the GPS module on with a predefined timeout. Use this if you want to let it warm up before sending a Find command.

It only accepts commands from a predefined number, all other inbound texts are ignored and reported. It also texts back confirmations and lets you know if you sent an invalid command.

Here is a copy of my current sketch, some photos and a diagram you can use to follow all the wiring.

[code]/*
This sketch is a combination of concepts from the Hologram Cloud demo sketch
https://github.com/hologram-io/hologram-dash-arduino-integration/blob/master/konektdash/libraries/DashExamples/examples/hologram_dash_cloud/hologram_dash_cloud.ino
and the SMS Controlled Remote Car Starter by Ben Jeffery
https://www.reddit.com/r/arduino/comments/2izu1t/completed_project_sms_controlled_remote_car/
https://https://github.com/ben-jeffery/sms-remote-starter/blob/master/SMS-Remote-Starter.ino
The confirmation messages back to the sender are handled by Routes set up in the Hologram Dashboard.
Thanks to cactus io for the BME280 hookup guide http://cactus.io/hookups/sensors/barometric/bme280/hookup-arduino-to-bme280-barometric-pressure-sensor
Special thanks to /dev for helping me get NeoGPS implemented and for teaching me how to use flags.
https://github.com/SlashDevin/NeoGPS
Make sure to run the NMEAorder sketch to set the last sentance being sent.
*/

#include <Wire.h>
#include “cactus_io_BME280_I2C.h”

// GPS items
#include <NMEAGPS.h>
NMEAGPS GPS;
gps_fix fix;
#define gpsPort Serial2
#define GPS_BAUD 9600 // GPS module baud rate.
bool waitingForFix = false;
bool offMessage = false; // Keeps the timeout message from being sent more than once.
bool turnGPSoff = false;
const unsigned long GPS_TIMEOUT = 600000; // 10 minutes after last Find command
unsigned long gpsStart = 0;
bool sleep = true;

// Key fob pins
const int StartPin = R03;
const int LockPin = R04;
const int UnlockPin = R05;
const int HornPin = R06;

// GPS switch pin
const int GPSpower = R07;

// Create the BME280 object
BME280_I2C bme(0x76); // I2C using address 0x76

// Set inbound number for filtering
String myNumber = “+18008675309”;

//Process a received SMS
void cloud_sms(const String &sender, const rtc_datetime_t &timestamp, const String &message) {
if (sender == myNumber) {

if (message == "Start") {
  remoteStart();
  HologramCloud.print("Remote Start Initiated");
  HologramCloud.attachTag("TaskMaster");
  HologramCloud.sendMessage();
}
else if (message == "Lock") {
  lock();
  HologramCloud.print("Doors Locked");
  HologramCloud.attachTag("TaskMaster");
  HologramCloud.sendMessage();
}
else if (message == "Unlock") {
  unlock();
  HologramCloud.print("Doors Unlocked");
  HologramCloud.attachTag("TaskMaster");
  HologramCloud.sendMessage();
}
else if (message == "Horn") {
  horn();
  HologramCloud.print("Horn Button Pressed");
  HologramCloud.attachTag("TaskMaster");
  HologramCloud.sendMessage();
}
else if (message == "Locate") {
  locate();
  HologramCloud.print("Horn Button Long Pressed");
  HologramCloud.attachTag("TaskMaster");
  HologramCloud.sendMessage();
}
else if (message == "Abort") {
  cancelStart();
  HologramCloud.print("Remote Start Aborted");
  HologramCloud.attachTag("TaskMaster");
  HologramCloud.sendMessage();
}
else if (message == "Ping") {
  HologramCloud.print("Pong");
  HologramCloud.attachTag("TaskMaster");
  HologramCloud.sendMessage();
}
else if (message == "Status") {
  bme.readSensor();
  HologramCloud.print("Temperature: ");
  HologramCloud.print(bme.getTemperature_F());
  HologramCloud.println(" F");
  HologramCloud.print("Humidity: ");
  HologramCloud.print(bme.getHumidity());
  HologramCloud.println("%");
  HologramCloud.print("Signal Strength: ");
  HologramCloud.println(HologramCloud.getSignalStrength());
  HologramCloud.print("Carrier: ");
  HologramCloud.println(HologramCloud.getNetworkOperator());
  //HologramCloud.print("Battery: ");
  //HologramCloud.print(FuelGauge.percentage());
  //HologramCloud.println("%");
 
  HologramCloud.attachTag("TaskMaster");
  HologramCloud.sendMessage();
}
else if (message == "GPSON") {
  digitalWrite(GPSpower, HIGH);
  sleep = false;
  offMessage = true;
  gpsStart = millis();
  HologramCloud.println("GPS Power On");
  HologramCloud.attachTag("TaskMaster");
  HologramCloud.sendMessage();
}
else if (message == "Find") {
  digitalWrite(GPSpower, HIGH);
  sleep = false;
  offMessage = true;
  gpsStart = millis();
  waitingForFix = true;
  HologramCloud.print("Waiting for Fix");
  HologramCloud.attachTag("TaskMaster");
  HologramCloud.sendMessage();
}
else if (message == "GPSOFF") {
  digitalWrite(GPSpower, LOW);
  HologramCloud.print("GPS Power Off");
  HologramCloud.attachTag("TaskMaster");
  HologramCloud.sendMessage();
  sleep = true;
  offMessage = false;
}
else {
  HologramCloud.print("Invalid Command");
  HologramCloud.attachTag("TaskMaster");
  HologramCloud.sendMessage();
}

}
else {
HologramCloud.println(“Unauthorized Inbound Message”);
HologramCloud.print("From: “);
HologramCloud.println(sender);
HologramCloud.println(timestamp);
HologramCloud.println(” Message: ");
HologramCloud.print(message);
HologramCloud.attachTag(“Unauthorized”);
HologramCloud.sendMessage();
}
}

void setup() {

if (!bme.begin()) {
HologramCloud.println(“Could not find a valid BME280 sensor, check wiring!”);
HologramCloud.attachTag(“TaskMaster”);
HologramCloud.sendMessage();
while (1);
}
//Sets Thermometer Offset
bme.setTempCal(0);

//GPS Port & Debug
gpsPort.begin(GPS_BAUD);
//Serial.begin(9600); //Debug port

//Set pins to Output mode
pinMode(StartPin, OUTPUT); //
pinMode(LockPin, OUTPUT); //
pinMode(UnlockPin, OUTPUT); //
pinMode(HornPin, OUTPUT); //
pinMode(GPSpower, OUTPUT); //

//Use the cloud_sms function to process incoming SMS
HologramCloud.attachHandlerSMS(cloud_sms);

//sync clock with network time
rtc_datetime_t dt;
if (HologramCloud.getNetworkTime(dt)) {
Clock.setDateTime(dt);
}
}

void loop() {
//Blink the LED while the Dash is awake
Dash.pulseLED(200, 200);
gpsHandler();
HologramCloud.pollEvents();
if (sleep) {
Dash.snooze(1500);
Dash.offLED();
Dash.sleep();
}

}

void lock() {
digitalWrite(LockPin, HIGH);
delay(500);
digitalWrite(LockPin, LOW);
delay(500);
digitalWrite(LockPin, HIGH);
delay(500);
digitalWrite(LockPin, LOW);
}
void unlock() {
digitalWrite(UnlockPin, HIGH);
delay(500);
digitalWrite(UnlockPin, LOW);
}
void horn() {
digitalWrite(HornPin, HIGH);
delay(500);
digitalWrite(HornPin, LOW);
}
void locate() {
digitalWrite(HornPin, HIGH);
delay(3000);
digitalWrite(HornPin, LOW);
}
void remoteStart() {
digitalWrite(LockPin, HIGH);
delay(1500);
digitalWrite(LockPin, LOW);
digitalWrite(StartPin, HIGH);
delay(3000);
digitalWrite(StartPin, LOW);
}
void cancelStart() {
digitalWrite(StartPin, HIGH);
delay(1500);
digitalWrite(StartPin, LOW);
}

void gpsHandler()
{
// Is a GPS fix available?
if (GPS.available( gpsPort )) {
fix = GPS.read();

if (waitingForFix) {
  //printGPSInfo();
  //Serial.println( millis() - gpsStart ); // DEBUG

  if (fix.valid.location) {
    mapLink();
    printGPSInfo2();
    waitingForFix = false;
    //turnGPSoff    = true; //enable if you want the GPS to power down immmediately after getting a fix and sending a message
  }
}

}

if (millis() - gpsStart > GPS_TIMEOUT) {
waitingForFix = false;
turnGPSoff = true;
}

if (turnGPSoff) {
digitalWrite(GPSpower, LOW);
if (offMessage) {
HologramCloud.print(“GPS Timeout - Shutting Down GPS”);
HologramCloud.attachTag(“TaskMaster”);
HologramCloud.sendMessage();
}
offMessage = false;
sleep = true;
turnGPSoff = false;
}
}

void printGPSInfo()
{
Serial.print( F("\nLat: “) );
if (fix.valid.location)
Serial.print( fix.latitude(), 6);
Serial.print( F(”\nLong: “) );
if (fix.valid.location)
Serial.print( fix.longitude(), 6);
Serial.print( F(”\nAlt: “) );
if (fix.valid.altitude)
Serial.print( fix.altitude() * 3.2808 );
Serial.print( F(”\nCourse: “) );
if (fix.valid.heading)
Serial.print(fix.heading());
Serial.print( F(”\nSpeed: “) );
if (fix.valid.speed)
Serial.print(fix.speed_mph());
Serial.print( F(”\nDate: “) );
if (fix.valid.date)
printDate();
Serial.print( F(”\nTime: “) );
if (fix.valid.time)
printTime();
Serial.print( F(”\nSats: ") );
if (fix.valid.satellites)
Serial.print(fix.satellites);
Serial.println(’\n’);
}

void printGPSInfo2()
{
HologramCloud.print( F("\nLat: “) );
if (fix.valid.location)
HologramCloud.print( fix.latitude(), 6);
HologramCloud.print( F(”\nLong: “) );
if (fix.valid.location)
HologramCloud.print( fix.longitude(), 6);
HologramCloud.print( F(”\nSpeed: “) );
if (fix.valid.speed)
HologramCloud.print(fix.speed_mph());
HologramCloud.print( F(“mph”) );
HologramCloud.print( F(”\nHeading: “) );
if (fix.valid.heading)
HologramCloud.print(fix.heading());
HologramCloud.print( F(”\nHDOP: “) );
if (fix.valid.hdop)
HologramCloud.print(fix.hdop);
HologramCloud.print( F(”\nSats: ") );
if (fix.valid.satellites)
HologramCloud.print(fix.satellites);
HologramCloud.println(’\n’);
HologramCloud.attachTag(“TaskMaster”);
HologramCloud.sendMessage();
}

// Sends location as a Google Maps pin link

void mapLink()
{
HologramCloud.print( F(“http://maps.google.com/maps?q=loc:”) );
if (fix.valid.location)
HologramCloud.print( fix.latitude(), 6);
HologramCloud.print( F(",") );
if (fix.valid.location)
HologramCloud.print( fix.longitude(), 6);
HologramCloud.attachTag(“TaskMaster”);
HologramCloud.sendMessage();
}

void printTime()
{
Serial.print(fix.dateTime.hours);
Serial.print(’:’);
if (fix.dateTime.minutes < 10) Serial.print(‘0’);
Serial.print(fix.dateTime.minutes);
Serial.print(’:’);
if (fix.dateTime.seconds < 10) Serial.print(‘0’);
Serial.print(fix.dateTime.seconds);
}

void printDate()
{
Serial.print(fix.dateTime.date);
Serial.print(’/’);
Serial.print(fix.dateTime.month);
Serial.print(’/’);
Serial.print(fix.dateTime.year);
}[/code]

Parts List:
1 Hologram Dash
1 BME280 Temperature Sensor
1 GPS Module (I used a Ublox M8N based module)
1 Extra Remote Fob
5 Resistors (I used 1k ohm)
5 NPN Transistors (I used S8550)

My next step is to put it all on a protoboard and into a small enclosure so it can have a permanent home hidden in the car. I am also going to connect it to a permanent gps antenna.

Many thanks to /dev over at the Arduino fourms who helped me implement his NeoGPS library and taught me how to use flags.


#2

I installed the GPS antenna under the dash this morning and it is getting a fix much faster now.

I also found an iOS app called Other that lets you set up predefined texts. It works well.


#3

Wow, this is an amazing project. Nice work! Thanks so much for sharing it with us.

How is it powered? Does it plug into the car or do you have a separate battery?


#4

Thanks – it has been a lot of fun figuring it out and learning along the way. I only got my first Arduino back in November. Right now it is running on a 12500mAh USB battery pack which lasts several days. I also tried a 2500mAh lipo battery connected to the dash but I couldn’t keep it charged being connected only when I was driving. Once I get it in the enclosure, I plan on getting one of the batteries made for dash cams that recharges when you drive. I found one that has a 7 amp recharge rate and that’s the only one I think will stay charged up all the time given how little I normally drive each day.

Also, in case anyone is wondering I measured it using one of those little USB meters and it draws .05 amps when sleeping and waiting for a message and .11 amps when operating with the GPS on.


#5

Jethro,

Wow, an amazing project. Thanks so much for sharing! Very solid work here, sir [tips hat].


#6

I recently had to update my code because the Google Maps link syntax I was using stopped working. I realized that I had made several tweaks since my original post so I thought I would post my updated sketch in case anyone finds it useful. It’s been running great and I am glad I did it.

/*
   This sketch is a combination of concepts from the Hologram Cloud demo sketch
   https://github.com/hologram-io/hologram-dash-arduino-integration/blob/master/konektdash/libraries/DashExamples/examples/hologram_dash_cloud/hologram_dash_cloud.ino
   and the SMS Controlled Remote Car Starter by Ben Jeffery
   https://www.reddit.com/r/arduino/comments/2izu1t/completed_project_sms_controlled_remote_car/
   https://https://github.com/ben-jeffery/sms-remote-starter/blob/master/SMS-Remote-Starter.ino
   The confirmation messages back to the sender are handled by Routes set up in the Hologram Dashboard.
   Thanks to cactus io for the BME280 hookup guide http://cactus.io/hookups/sensors/barometric/bme280/hookup-arduino-to-bme280-barometric-pressure-sensor
   Special thanks to /dev for helping me get NeoGPS implemented and for teaching me how to use flags.
   https://github.com/SlashDevin/NeoGPS
   Make sure to run the NMEAorder sketch to set the last sentance being sent.
*/

#include <Wire.h>
#include "cactus_io_BME280_I2C.h"

// GPS items
#include <NMEAGPS.h>
NMEAGPS GPS;
gps_fix fix;
#define gpsPort Serial2
#define GPS_BAUD 9600    // GPS module baud rate. 
bool waitingForFix = false;
bool offMessage = false; // Keeps the timeout message from being sent more than once.
bool turnGPSoff = false;
const unsigned long GPS_TIMEOUT   = 600000; // 10 minutes after last Find command
unsigned long gpsStart = 0;
bool sleep = true;

// Key fob pins
const int StartPin = R03;
const int LockPin = R04;
const int UnlockPin = R05;
const int HornPin = R06;

// GPS switch pin
const int GPSpower = R07;

// Create the BME280 object
BME280_I2C bme(0x76);  // I2C using address 0x76

// Set inbound number for filtering
String myNumber = "+18008675309";

//Process a received SMS
void cloud_sms(const String &sender, const rtc_datetime_t &timestamp, const String &message) {
  if (sender == myNumber) {

    if (message == "Start") {
      remoteStart();
      HologramCloud.print("Remote Start Initiated");
      HologramCloud.attachTag("TaskMaster");
      HologramCloud.sendMessage();
    }
    else if (message == "Lock") {
      lock();
      HologramCloud.print("Doors Locked");
      HologramCloud.attachTag("TaskMaster");
      HologramCloud.sendMessage();
    }
    else if (message == "Unlock") {
      unlock();
      HologramCloud.print("Doors Unlocked");
      HologramCloud.attachTag("TaskMaster");
      HologramCloud.sendMessage();
    }
    else if (message == "Horn") {
      horn();
      HologramCloud.print("Horn Button Pressed");
      HologramCloud.attachTag("TaskMaster");
      HologramCloud.sendMessage();
    }
    else if (message == "Locate") {
      locate();
      HologramCloud.print("Horn Button Long Pressed");
      HologramCloud.attachTag("TaskMaster");
      HologramCloud.sendMessage();
    }
    else if (message == "Abort") {
      cancelStart();
      HologramCloud.print("Remote Start Aborted");
      HologramCloud.attachTag("TaskMaster");
      HologramCloud.sendMessage();
    }
    else if (message == "Ping") {
      HologramCloud.print("Pong");
      HologramCloud.attachTag("TaskMaster");
      HologramCloud.sendMessage();
    }
    else if (message == "Status") {
      bme.readSensor();
      HologramCloud.print("Temp: ");
      HologramCloud.print(bme.getTemperature_F());
      HologramCloud.println(" F");
      HologramCloud.print("Humidity: ");
      HologramCloud.print(bme.getHumidity());
      HologramCloud.println("%");
      HologramCloud.print("Carrier: ");
      HologramCloud.println(HologramCloud.getNetworkOperator());
      HologramCloud.print("Signal: ");
      HologramCloud.println(HologramCloud.getSignalStrength());
      HologramCloud.attachTag("TaskMaster");
      HologramCloud.sendMessage();
    }
    else if (message == "GPSON") {
      digitalWrite(GPSpower, HIGH);
      sleep = false;
      offMessage = true;
      gpsStart = millis();
      HologramCloud.println("GPS Power On");
      HologramCloud.attachTag("TaskMaster");
      HologramCloud.sendMessage();
    }
    else if (message == "Find") {
      digitalWrite(GPSpower, HIGH);
      sleep = false;
      offMessage = true;
      gpsStart = millis();
      waitingForFix = true;
      HologramCloud.print("Waiting for Fix");
      HologramCloud.attachTag("TaskMaster");
      HologramCloud.sendMessage();
    }
    else if (message == "GPSOFF") {
      digitalWrite(GPSpower, LOW);
      HologramCloud.print("GPS Power Off");
      HologramCloud.attachTag("TaskMaster");
      HologramCloud.sendMessage();
      sleep = true;
      offMessage = false;
    }
    else {
      HologramCloud.print("Invalid Command");
      HologramCloud.attachTag("TaskMaster");
      HologramCloud.sendMessage();
    }

  }
  else {
    HologramCloud.println("Unauthorized Inbound Message");
    HologramCloud.print("From: ");
    HologramCloud.println(sender);
    HologramCloud.println(timestamp);
    HologramCloud.println(" Message: ");
    HologramCloud.print(message);
    HologramCloud.attachTag("Unauthorized");
    HologramCloud.sendMessage();
  }
}

void setup() {

  if (!bme.begin()) {
    HologramCloud.println("Could not find a valid BME280 sensor, check wiring!");
    HologramCloud.attachTag("TaskMaster");
    HologramCloud.sendMessage();
    while (1);
  }
  //Sets Thermometer Offset
  bme.setTempCal(0);

  //GPS Port & Debug
  gpsPort.begin(GPS_BAUD);
  Serial.begin(9600); //Debug port
  //Will connect to the Hologram Cloud automatically
  HologramCloud.sendMessage("Dash has booted", "TaskMaster");



  //Set pins to Output mode
  pinMode(StartPin, OUTPUT);  //
  pinMode(LockPin, OUTPUT);  //
  pinMode(UnlockPin, OUTPUT);  //
  pinMode(HornPin, OUTPUT);  //
  pinMode(GPSpower, OUTPUT); //

  //Use the cloud_sms function to process incoming SMS
  HologramCloud.attachHandlerSMS(cloud_sms);

  //sync clock with network time
  rtc_datetime_t dt;
  if (HologramCloud.getNetworkTime(dt)) {
    Clock.setDateTime(dt);
  }
}

void loop() {
  //Blink the LED while the Dash is awake
  Dash.pulseLED(200,200);
  gpsHandler();
  HologramCloud.connect();
  //HologramCloud.pollEvents();
  
  //For Debugging Network Issues
  Serial.print("Signal Strength: ");
  Serial.println(HologramCloud.getSignalStrength());
  Serial.print("Carrier: ");
  Serial.println(HologramCloud.getNetworkOperator());
  Serial.print("Status: ");
  Serial.println(HologramCloud.getConnectionStatus());

  }

void lock() {
  digitalWrite(LockPin, HIGH);
  delay(500);
  digitalWrite(LockPin, LOW);
  delay(500);
  digitalWrite(LockPin, HIGH);
  delay(500);
  digitalWrite(LockPin, LOW);
}
void unlock() {
  digitalWrite(UnlockPin, HIGH);
  delay(500);
  digitalWrite(UnlockPin, LOW);
}
void horn() {
  digitalWrite(HornPin, HIGH);
  delay(500);
  digitalWrite(HornPin, LOW);
}
void locate() {
  digitalWrite(HornPin, HIGH);
  delay(3000);
  digitalWrite(HornPin, LOW);
}
void remoteStart() {
  digitalWrite(LockPin, HIGH);
  delay(1500);
  digitalWrite(LockPin, LOW);
  digitalWrite(StartPin, HIGH);
  delay(3000);
  digitalWrite(StartPin, LOW);
}
void cancelStart() {
  digitalWrite(StartPin, HIGH);
  delay(1500);
  digitalWrite(StartPin, LOW);
}


void gpsHandler()
{
  // Is a GPS fix available?
  if (GPS.available( gpsPort )) {
    fix = GPS.read();

    if (waitingForFix) {
      //printGPSInfo();
      //Serial.println( millis() - gpsStart ); // DEBUG

      if (fix.valid.location) {
        mapLink();
        printGPSInfo2();
        waitingForFix = false;
        //turnGPSoff    = true; //enable if you want the GPS to power down immmediately after getting a fix and sending a message
      }
    }
  }

  if (millis() - gpsStart > GPS_TIMEOUT) {
    waitingForFix = false;
    turnGPSoff    = true;
  }

  if (turnGPSoff) {
    digitalWrite(GPSpower, LOW);
    if (offMessage) {
      HologramCloud.print("GPS Timeout - Shutting Down GPS");
      HologramCloud.attachTag("TaskMaster");
      HologramCloud.sendMessage();
    }
    offMessage = false;
    sleep = true;
    turnGPSoff = false;
  }
}


void printGPSInfo()
{
  Serial.print( F("\nLat: ") );
  if (fix.valid.location)
    Serial.print( fix.latitude(), 6);
  Serial.print( F("\nLong: ") );
  if (fix.valid.location)
    Serial.print( fix.longitude(), 6);
  Serial.print( F("\nAlt: ") );
  if (fix.valid.altitude)
    Serial.print( fix.altitude() * 3.2808 );
  Serial.print( F("\nCourse: ") );
  if (fix.valid.heading)
    Serial.print(fix.heading());
  Serial.print( F("\nSpeed: ") );
  if (fix.valid.speed)
    Serial.print(fix.speed_mph());
  Serial.print( F("\nDate: ") );
  if (fix.valid.date)
    printDate();
  Serial.print( F("\nTime: ") );
  if (fix.valid.time)
    printTime();
  Serial.print( F("\nSats: ") );
  if (fix.valid.satellites)
    Serial.print(fix.satellites);
  Serial.println('\n');
}

void printGPSInfo2()
{
  HologramCloud.print( F("\nLat: ") );
  if (fix.valid.location)
    HologramCloud.print( fix.latitude(), 6);
  HologramCloud.print( F("\nLong: ") );
  if (fix.valid.location)
    HologramCloud.print( fix.longitude(), 6);
  HologramCloud.print( F("\nSpeed: ") );
  if (fix.valid.speed)
    HologramCloud.print(fix.speed_mph());
  HologramCloud.print( F("mph") );
  HologramCloud.print( F("\nHeading: ") );
  if (fix.valid.heading)
    HologramCloud.print(fix.heading());
  HologramCloud.print( F("\nHDOP: ") );
  if (fix.valid.hdop)
    HologramCloud.print(fix.hdop);
  HologramCloud.print( F("\nSats: ") );
  if (fix.valid.satellites)
    HologramCloud.print(fix.satellites);
  HologramCloud.println('\n');
  HologramCloud.attachTag("TaskMaster");
  HologramCloud.sendMessage();
}

// Sends location as a Google Maps pin link

void mapLink()
{
  HologramCloud.print( F("https://www.google.com/maps/search/?api=1&query=") );
  if (fix.valid.location)
    HologramCloud.print( fix.latitude(), 6);
  HologramCloud.print( F(",") );
  if (fix.valid.location)
    HologramCloud.print( fix.longitude(), 6);
  HologramCloud.attachTag("TaskMaster");
  HologramCloud.sendMessage();
}

void printTime()
{
  Serial.print(fix.dateTime.hours);
  Serial.print(':');
  if (fix.dateTime.minutes < 10) Serial.print('0');
  Serial.print(fix.dateTime.minutes);
  Serial.print(':');
  if (fix.dateTime.seconds < 10) Serial.print('0');
  Serial.print(fix.dateTime.seconds);
}

void printDate()
{
  Serial.print(fix.dateTime.date);
  Serial.print('/');
  Serial.print(fix.dateTime.month);
  Serial.print('/');
  Serial.print(fix.dateTime.year);
}