SD_GPSLogger.ino
1 // SPDX-FileCopyrightText: Bill Greiman for Adafruit Industries 2 // 3 // SPDX-License-Identifier: MIT 4 5 // Ladyada's logger modified by Bill Greiman to use the SdFat library 6 7 // this is a generic logger that does checksum testing so the data written should be always good 8 // Assumes a sirf III chipset logger attached to pin 2 and 3 9 10 #include <SD.h> 11 #include <avr/sleep.h> 12 #include "GPSconfig.h" 13 14 // If using Arduino IDE prior to 1.0, 15 // make sure to install newsoftserial from Mikal Hart 16 // http://arduiniana.org/libraries/NewSoftSerial/ 17 #if ARDUINO >= 100 18 #include <SoftwareSerial.h> 19 #else 20 #include <NewSoftSerial.h> 21 #endif 22 23 // power saving modes 24 #define SLEEPDELAY 0 /* power-down time in seconds. Max 65535. Ignored if TURNOFFGPS == 0 */ 25 #define TURNOFFGPS 0 /* set to 1 to enable powerdown of arduino and GPS. Ignored if SLEEPDELAY == 0 */ 26 #define LOG_RMC_FIXONLY 0 /* set to 1 to only log to SD when GPD has a fix */ 27 28 // what to log 29 #define LOG_RMC 1 // RMC-Recommended Minimum Specific GNSS Data, message 103,04 30 #define LOG_GGA 0 // GGA-Global Positioning System Fixed Data, message 103,00 31 #define LOG_GLL 0 // GLL-Geographic Position-Latitude/Longitude, message 103,01 32 #define LOG_GSA 0 // GSA-GNSS DOP and Active Satellites, message 103,02 33 #define LOG_GSV 0 // GSV-GNSS Satellites in View, message 103,03 34 #define LOG_VTG 0 // VTG-Course Over Ground and Ground Speed, message 103,05 35 36 // Use pins 2 and 3 to talk to the GPS. 2 is the TX pin, 3 is the RX pin 37 #if ARDUINO >= 100 38 SoftwareSerial gpsSerial = SoftwareSerial(2, 3); 39 #else 40 NewSoftSerial gpsSerial = NewSoftSerial(2, 3); 41 #endif 42 // Set the GPSRATE to the baud rate of the GPS module. Most are 4800 43 // but some are 38400 or other. Check the datasheet! 44 #define GPSRATE 4800 45 46 // Set the pins used 47 #define powerPin 4 48 #define led1Pin 5 49 #define led2Pin 6 50 #define chipSelect 10 51 52 53 #define BUFFSIZE 90 54 char buffer[BUFFSIZE]; 55 uint8_t bufferidx = 0; 56 bool fix = false; // current fix data 57 bool gotGPRMC; //true if current data is a GPRMC strinng 58 uint8_t i; 59 File logfile; 60 61 // read a Hex value and return the decimal equivalent 62 uint8_t parseHex(char c) { 63 if (c < '0') 64 return 0; 65 if (c <= '9') 66 return c - '0'; 67 if (c < 'A') 68 return 0; 69 if (c <= 'F') 70 return (c - 'A')+10; 71 } 72 73 // blink out an error code 74 void error(uint8_t errno) { 75 /* 76 if (SD.errorCode()) { 77 putstring("SD error: "); 78 Serial.print(card.errorCode(), HEX); 79 Serial.print(','); 80 Serial.println(card.errorData(), HEX); 81 } 82 */ 83 while(1) { 84 for (i=0; i<errno; i++) { 85 digitalWrite(led1Pin, HIGH); 86 digitalWrite(led2Pin, HIGH); 87 delay(100); 88 digitalWrite(led1Pin, LOW); 89 digitalWrite(led2Pin, LOW); 90 delay(100); 91 } 92 for (; i<10; i++) { 93 delay(200); 94 } 95 } 96 } 97 98 void setup() { 99 WDTCSR |= (1 << WDCE) | (1 << WDE); 100 WDTCSR = 0; 101 Serial.begin(9600); 102 Serial.println("\r\nGPSlogger"); 103 pinMode(led1Pin, OUTPUT); 104 pinMode(led2Pin, OUTPUT); 105 pinMode(powerPin, OUTPUT); 106 digitalWrite(powerPin, LOW); 107 108 // make sure that the default chip select pin is set to 109 // output, even if you don't use it: 110 pinMode(10, OUTPUT); 111 112 // see if the card is present and can be initialized: 113 if (!SD.begin(chipSelect)) { 114 Serial.println("Card init. failed!"); 115 error(1); 116 } 117 118 strcpy(buffer, "GPSLOG00.TXT"); 119 for (i = 0; i < 100; i++) { 120 buffer[6] = '0' + i/10; 121 buffer[7] = '0' + i%10; 122 // create if does not exist, do not open existing, write, sync after write 123 if (! SD.exists(buffer)) { 124 break; 125 } 126 } 127 128 logfile = SD.open(buffer, FILE_WRITE); 129 if( ! logfile ) { 130 Serial.print("Couldnt create "); Serial.println(buffer); 131 error(3); 132 } 133 Serial.print("Writing to "); Serial.println(buffer); 134 135 // connect to the GPS at the desired rate 136 gpsSerial.begin(GPSRATE); 137 138 Serial.println("Ready!"); 139 140 gpsSerial.print(SERIAL_SET); 141 delay(250); 142 143 #if (LOG_DDM == 1) 144 gpsSerial.print(DDM_ON); 145 #else 146 gpsSerial.print(DDM_OFF); 147 #endif 148 delay(250); 149 #if (LOG_GGA == 1) 150 gpsSerial.print(GGA_ON); 151 #else 152 gpsSerial.print(GGA_OFF); 153 #endif 154 delay(250); 155 #if (LOG_GLL == 1) 156 gpsSerial.print(GLL_ON); 157 #else 158 gpsSerial.print(GLL_OFF); 159 #endif 160 delay(250); 161 #if (LOG_GSA == 1) 162 gpsSerial.print(GSA_ON); 163 #else 164 gpsSerial.print(GSA_OFF); 165 #endif 166 delay(250); 167 #if (LOG_GSV == 1) 168 gpsSerial.print(GSV_ON); 169 #else 170 gpsSerial.print(GSV_OFF); 171 #endif 172 delay(250); 173 #if (LOG_RMC == 1) 174 gpsSerial.print(RMC_ON); 175 #else 176 gpsSerial.print(RMC_OFF); 177 #endif 178 delay(250); 179 180 #if (LOG_VTG == 1) 181 gpsSerial.print(VTG_ON); 182 #else 183 gpsSerial.print(VTG_OFF); 184 #endif 185 delay(250); 186 187 #if (USE_WAAS == 1) 188 gpsSerial.print(WAAS_ON); 189 #else 190 gpsSerial.print(WAAS_OFF); 191 #endif 192 } 193 194 void loop() { 195 //Serial.println(Serial.available(), DEC); 196 char c; 197 uint8_t sum; 198 199 // read one 'line' 200 if (gpsSerial.available()) { 201 c = gpsSerial.read(); 202 #if ARDUINO >= 100 203 //Serial.write(c); 204 #else 205 //Serial.print(c, BYTE); 206 #endif 207 if (bufferidx == 0) { 208 while (c != '$') 209 c = gpsSerial.read(); // wait till we get a $ 210 } 211 buffer[bufferidx] = c; 212 213 #if ARDUINO >= 100 214 //Serial.write(c); 215 #else 216 //Serial.print(c, BYTE); 217 #endif 218 if (c == '\n') { 219 //putstring_nl("EOL"); 220 //Serial.print(buffer); 221 buffer[bufferidx+1] = 0; // terminate it 222 223 if (buffer[bufferidx-4] != '*') { 224 // no checksum? 225 Serial.print('*'); 226 bufferidx = 0; 227 return; 228 } 229 // get checksum 230 sum = parseHex(buffer[bufferidx-3]) * 16; 231 sum += parseHex(buffer[bufferidx-2]); 232 233 // check checksum 234 for (i=1; i < (bufferidx-4); i++) { 235 sum ^= buffer[i]; 236 } 237 if (sum != 0) { 238 //putstring_nl("Cxsum mismatch"); 239 Serial.print('~'); 240 bufferidx = 0; 241 return; 242 } 243 // got good data! 244 245 gotGPRMC = strstr(buffer, "GPRMC"); 246 if (gotGPRMC) { 247 // find out if we got a fix 248 char *p = buffer; 249 p = strchr(p, ',')+1; 250 p = strchr(p, ',')+1; // skip to 3rd item 251 252 if (p[0] == 'V') { 253 digitalWrite(led1Pin, LOW); 254 fix = false; 255 } else { 256 digitalWrite(led1Pin, HIGH); 257 fix = true; 258 } 259 } 260 if (LOG_RMC_FIXONLY) { 261 if (!fix) { 262 Serial.print('_'); 263 bufferidx = 0; 264 return; 265 } 266 } 267 // rad. lets log it! 268 269 Serial.print(buffer); //first, write it to the serial monitor 270 Serial.print('#'); 271 272 if (gotGPRMC) //If we have a GPRMC string 273 { 274 // Bill Greiman - need to write bufferidx + 1 bytes to getCR/LF 275 bufferidx++; 276 277 digitalWrite(led2Pin, HIGH); // Turn on LED 2 (indicates write to SD) 278 279 logfile.write((uint8_t *) buffer, bufferidx); //write the string to the SD file 280 logfile.flush(); 281 /* 282 if( != bufferidx) { 283 putstring_nl("can't write!"); 284 error(4); 285 } 286 */ 287 288 digitalWrite(led2Pin, LOW); //turn off LED2 (write to SD is finished) 289 290 bufferidx = 0; //reset buffer pointer 291 292 if (fix) { //(don't sleep if there's no fix) 293 294 if ((TURNOFFGPS) && (SLEEPDELAY)) { // turn off GPS module? 295 296 digitalWrite(powerPin, HIGH); //turn off GPS 297 298 delay(100); //wait for serial monitor write to finish 299 sleep_sec(SLEEPDELAY); //turn off CPU 300 301 digitalWrite(powerPin, LOW); //turn on GPS 302 } //if (TURNOFFGPS) 303 304 } //if (fix) 305 306 return; 307 }//if (gotGPRMC) 308 309 } 310 bufferidx++; 311 if (bufferidx == BUFFSIZE-1) { 312 Serial.print('!'); 313 bufferidx = 0; 314 } 315 } else { 316 317 } 318 319 } 320 321 void sleep_sec(uint16_t x) { 322 while (x--) { 323 // set the WDT to wake us up! 324 WDTCSR |= (1 << WDCE) | (1 << WDE); // enable watchdog & enable changing it 325 WDTCSR = (1<< WDE) | (1 <<WDP2) | (1 << WDP1); 326 WDTCSR |= (1<< WDIE); 327 set_sleep_mode(SLEEP_MODE_PWR_DOWN); 328 // sleep_enable(); 329 sleep_mode(); 330 // sleep_disable(); 331 } 332 } 333 334 SIGNAL(WDT_vect) { 335 WDTCSR |= (1 << WDCE) | (1 << WDE); 336 WDTCSR = 0; 337 } 338 339 /* End code */