/ Track_Your_Treats / Track_Your_Treats / Track_Your_Treats.ino
Track_Your_Treats.ino
  1  // SPDX-FileCopyrightText: 2019 Tony DiCola for Adafruit Industries
  2  //
  3  // SPDX-License-Identifier: MIT
  4  
  5  // Track Your Treats - Ultimate GPS Shield Halloween Candy Route Tracker
  6  // Author: Tony DiCola
  7  //
  8  // See the guide at:
  9  // https://learn.adafruit.com/track-your-treats-halloween-candy-gps-tracker/overview
 10  // 
 11  // Released under a MIT license:
 12  // https://opensource.org/licenses/MIT
 13  #include <SPI.h>
 14  #include <Adafruit_GPS.h>
 15  #include <SoftwareSerial.h>
 16  #include <SD.h>
 17  
 18  
 19  // Configuration (you don't normally need to change these values):
 20  
 21  #define LED_PIN              6   // Pin connected to an LED that flashes the status of the project.
 22  
 23  #define BUTTON_PIN           5   // Pin connected to the button.
 24  
 25  #define LOGGING_PERIOD_SEC   15  // Seconds to wait between logging GPS locations.
 26  
 27  #define GPS_RX_PIN           8   // GPS receiver RX (pin 8 on the shield).
 28  
 29  #define GPS_TX_PIN           7   // GPS receiver TX (pin 7 on the shield)   
 30  
 31  #define SD_CS_PIN            10  // Chip select pin for SD card.
 32  
 33  
 34  // Global state (you don't need to change these values):
 35  SoftwareSerial gpsSerial(GPS_RX_PIN, GPS_TX_PIN);  // Software serial connection to GPS receiver.
 36  Adafruit_GPS GPS(&gpsSerial);                      // GPS class to interact with receiver.
 37  File logfile;                                      // SD card log file.
 38  uint32_t logCounter = 0;                           // Counter until next location log is recorded.
 39  
 40  
 41  // Halt function called when an error occurs.  Will print an error and stop execution while
 42  // doing a fast blink of the LED.  If the watchdog is enabled it will reset after 8 seconds.
 43  void halt(const __FlashStringHelper *error) {
 44    Serial.println(error);
 45    while (1) {
 46      digitalWrite(LED_PIN, LOW);
 47      delay(100); 
 48      digitalWrite(LED_PIN, HIGH);
 49      delay(100);
 50    }
 51  }
 52  
 53  // Timer interrupt called every millisecond to check for new data from the GPS.
 54  SIGNAL(TIMER0_COMPA_vect) {
 55    // Check for new GPS data.
 56    GPS.read();
 57    // Decrease the count since last location log.
 58    if (logCounter > 0) {
 59      logCounter--;
 60    }
 61  }
 62  
 63  // Log the current GPS location with the specified note.
 64  void logLocation(const char* note) {
 65    logfile.print(GPS.latitudeDegrees, 6);
 66    logfile.print(',');
 67    logfile.print(GPS.longitudeDegrees, 6);
 68    logfile.print(',');
 69    logfile.print(note);
 70    logfile.println();
 71    logfile.flush();
 72  }
 73  
 74  void setup() {
 75    // Initialize serial port.
 76    Serial.begin(115200);
 77    Serial.println(F("Track your Treats - Ultimate GPS Shield"));
 78  
 79    // Initialize LED and button.
 80    pinMode(LED_PIN, OUTPUT);
 81    pinMode(BUTTON_PIN, INPUT_PULLUP);
 82  
 83    // make sure that the default chip select pin is set to
 84    // output, even if you don't use it:
 85    pinMode(SD_CS_PIN, OUTPUT);
 86  
 87    // Initialize SD card (assumes running on Uno, for Leonardo see below).
 88    if (!SD.begin(SD_CS_PIN)) {
 89    // Initialize SD card for Leonardo or other chips (using software SPI)
 90    //if (!SD.begin(SD_CS_PIN, 11, 12, 13)) {
 91      halt(F("Card init. failed!"));
 92    }
 93  
 94    // Create the next log file on the SD card.
 95    char filename[15];
 96    strcpy(filename, "GPSLOG00.CSV");
 97    for (uint8_t i = 0; i < 100; i++) {
 98      filename[6] = '0' + i/10;
 99      filename[7] = '0' + i%10;
100      // Create file if it does not exist.
101      if (!SD.exists(filename)) {
102        break;
103      }
104    }
105    Serial.print("Using log file: "); 
106    Serial.println(filename);
107  
108    // Open the log file.
109    logfile = SD.open(filename, FILE_WRITE);
110    if(!logfile) {
111      halt(F("Failed to open log file!"));
112    }
113  
114    // Set the first line of the log file as the column headers.
115    logfile.println("latitude,longitude,note");
116    logfile.flush();
117  
118    // Connect to the GPS receiver and configure it to receive location updates once a second.
119    GPS.begin(9600);
120    GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY); // Recommended minimum output only (all we need for this project).
121    GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);     // Once a second update rate.
122    GPS.sendCommand(PGCMD_NOANTENNA);              // Turn off antenna status.
123   
124    // Configure timer0 compare interrupt to run and parse GPS data every millisecond.
125    // See the SIGNAL function further below for the code that is called during this interrupt.
126    OCR0A = 0xAF;
127    TIMSK0 |= _BV(OCIE0A);
128  
129    Serial.println("Ready!");
130  }
131  
132  void loop() {
133    // Parse GPS messages when they are received.
134    if (GPS.newNMEAreceived()) {
135      GPS.parse(GPS.lastNMEA());
136    }
137    
138    // Light the LED solid if there's a GPS fix, otherwise flash it on and off once a second.
139    if (GPS.fix) {
140      digitalWrite(LED_PIN, HIGH);
141    }
142    else {
143      // No fix, blink the LED once a second and stop further processing.
144      digitalWrite(LED_PIN, (millis()/1000) % 2);
145      return;
146    }
147  
148    // Check if the button is pressed.
149    if (digitalRead(BUTTON_PIN) == LOW) {
150      // Pause a bit to debounce.
151      delay(100);
152      if (digitalRead(BUTTON_PIN) == LOW) {
153        // Button pressed! Log the current location as having good candy.
154        logLocation("Good Candy");
155        // Then flash the light 5 times to signal the location was recorded.
156        for (int i=0; i<5; ++i) {
157          digitalWrite(LED_PIN, HIGH);
158          delay(250);
159          digitalWrite(LED_PIN, LOW);
160          delay(250);
161        }
162      }
163    }
164  
165    // Periodically log the location.
166    if (logCounter == 0) {
167      logLocation("Location");
168      logCounter = LOGGING_PERIOD_SEC*1000;
169    }
170  }