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 }