A newbie here and my first ever post here. Please be supportive. And please bear with any silly mistakes I may have made in this post. So first thing first, here is my objective: I want to log GPS location and ancillary data at relatively low sample rate (say once every 3-5 seconds) on a MicroSD card.
And here is my hardware:
-
A strip board based ATmega328p running at 8Mhz internal clock, powered by regulated 3.3V (otherwise running perfectly with most other sketches),
-
GPS breakout (I alternate between a uBlox Neo-6 from eBay, and Adafruit Ultimate GPS, basically no significant difference here, both perfectly spitting valid NMEA sentences and getting fix in short time)
-
A microSd breakout (which simply exposes MiscroSD pins to a header friendly 0.1" spaced pins, absolutely no other circuit involved)
-
An ordinary 2GB OEM MicroSd card formatted by SD formatter utility (otherwise perfectly working with another sketch at higher sampling/logging rates, i.e., combined DHT11 Humidity&Temp sensor + SR04 Ping sensor at 8-10 samples per second).
Now here is what my problem is:
SD card is detected, file opened, data logging starts - but cannot proceed as expected. Any of these things happen randomly- file content gets garbled (unexpected things including extended ASCII symbols get written, no consistent end-of-line, even some of my Serial.print stuff gets written here when I keep Serial output ON).
I assume, I have no hardware, electrical, electromagnetic, wiring, bad SD card, or wrong formatting issues based on consistent successes I have with other sketches. That brings me down to this particular GPS data logger sketch. It contains SoftwareSerial.h, TinyGPS++.h and SdFat.h. I know all three are well written, widely used, well tested and well run libraries, there shouldn't be any question. So, my codes must have the root of the problem, unfortunately I cannot figure out what is the culprit.
I have checked available RAM at runtime and optimized RAM usage as much as I could. Now It shows FreeMem()=798 bytes in Setup(), and FreeMem()=792 bytes in Loop(). I have no clue what else I can do to solve my problem.
Here goes my code:
/*
GPS data logger using TinyGPS++ and SoftwareSerial libraries and examples by Mikal Hart
Sketch written by: Sayedur R Chowdhury, Dec 10, 2013
Data to be logged: Date, Time, Lat, Lon, Altitude, DOP, Speed, Bearing
Indicator LED:
Uses one common anode RGB LED. Connections:
Anode = pin 6
Red = pin 5
Green = pin 7
Blue = pin 8
GPS connections:
Rx = pin 2
Tx = pin 3
MicroSD connections:
CS = pin 10
MOSI = pin 11
MISO = pin 12
SCK = pin 13
*/
//#define SERIALOUT
//#include <MemoryFree.h>
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <SdFat.h>
//definitions fo SoftwareSerial GPS
static const byte Rx = 3, Tx = 2; //rx,tx (3,2) on the Arduino side connects to Tx, Rx of GPS respectively
static const int baudrate = 9600;
//defintion for RGB LED
static const byte commonAnode=6, R=5, G=7, B=8;
//definitions for SD card and file
SdFat sd;
SdFile datafile;
static const int SD_CS=10;
bool writeHeader=1;
//TinyGPS NMEA stream
TinyGPSPlus nmea;
//TinyGPSCustom pdop(nmea, "GPGSA", 15); //takes up 50 bytes of SRAM
//serial connection to the GPS device
SoftwareSerial gps(Rx, Tx);
void setup()
{
#ifdef SERIALOUT
Serial.begin(9600);
#endif
//set up RGB led pins
pinMode(commonAnode, OUTPUT);
pinMode(R, OUTPUT);
pinMode(G, OUTPUT);
pinMode(B, OUTPUT);
digitalWrite(commonAnode, HIGH);
digitalWrite(R, HIGH);
digitalWrite(G, HIGH);
digitalWrite(B, HIGH);
gps.begin(baudrate);
pinMode(SD_CS, OUTPUT);
if (sd.begin(SD_CS, SPI_HALF_SPEED)) {
//indicate success by long flashing ok led twice
flashLED(1000, G);
smartDelay(500);
flashLED(1000, G);
#ifdef SERIALOUT
Serial.println(F("SD Ok"));
#endif
}
else {
//indicate SD init error by long flashing error-led once
flashLED(1000, R);
#ifdef SERIALOUT
Serial.println(F("SD Error"));
#endif
} //SD.begin ends
#ifdef SERIALOUT
Serial.print("freeMemory()=");
Serial.println(freeMemory());
#endif
} //setup ends
void loop()
{
unsigned int pastCharProcessed=nmea.charsProcessed(), recentCharProcessed=0;
int flashTimer=0;
long previousMillis = 0;
unsigned long currentMillis = millis();
//SdFile datafile;
//smartdelay at the beginning
smartDelay(3000);
//check new nmea.charProcessed because smartDelay has already elapsed some time
recentCharProcessed=nmea.charsProcessed()-pastCharProcessed;
flashTimer=currentMillis-previousMillis;
//verify if GPS data coming in
if (recentCharProcessed < 10) { //not enough data coming in
//flash errorLed every 3 seconds if no data is coming, diagnose wiring problem
if (flashTimer > 3000) {
previousMillis = currentMillis;
flashLED(10, R);
flashTimer=0; //reset timer
#ifdef SERIALOUT
Serial.println(F('No data'));
#endif
}
}
else { //expected amount of data is coming
//verify if a fix is obtained
if (!nmea.location.isValid()) { //no fix yet
//flash fixLed every 10 seconds if data is coming but no fix yet
if (flashTimer > 10000) {
previousMillis = currentMillis;
flashLED(10, B);
flashTimer=0; //reset timer
#ifdef SERIALOUT
Serial.println(F('Data Ok, no fix'));
#endif
}
}
else { //fix obtained
//GPS units generally have fix indicators, an indicator is not needed; comment out flashLed codes below
/*
if (flashTimer > 30000) {
previousMillis = currentMillis;
flashLED(10, G);
flashTimer=0; //reset timer
}
*/
#ifdef SERIALOUT
Serial.println(F('Fix obtained'));
#endif
//Debug: the following closing brace moved here from below to initiate loggin even before a fix
//in the final sketch logging will start only when a fix is obtained
} //!nmea.location.isValid()
//now that a fix is obtained, proceed onto logging data to SD card file
if (datafile.open("GPSDATA.CSV", O_WRITE | O_CREAT | O_APPEND)) {
//indicate file success by flashing OkLed once
//following conditional LED flashing will change into unconditional green in final sketch
if (nmea.location.isValid()) {
flashLEDbicolor(20, R, B);
} else {
flashLED(20, G);
}
#ifdef SERIALOUT
Serial.println(F("GPSDATA.CSV opened"));
#endif
//write CSV header only once at the beginning of the loop
if (writeHeader==1) {
datafile.println();
datafile.print(F("GPS data logging starts at "));
datafile.print(millis());
datafile.println(F(" mSecs"));
datafile.println(F("date,time,lat,lon,altitude,DOP,speed,course"));
writeHeader=0;
}
//date-time
if (nmea.date.isUpdated()) {
datafile.print(nmea.date.year());
datafile.print("/");
datafile.print(nmea.date.month());
datafile.print("/");
datafile.print(nmea.date.day());
}
datafile.print(",");
if (nmea.time.isUpdated()) {
datafile.print(nmea.time.hour());
datafile.print(":");
datafile.print(nmea.time.minute());
datafile.print(":");
datafile.print(nmea.time.second());
datafile.print(".");
datafile.print(nmea.time.centisecond());
}
datafile.print(",");
//location
datafile.print(nmea.location.lat(),6);
datafile.print(",");
datafile.print(nmea.location.lng(),6);
datafile.print(",");
//altitude
datafile.print(nmea.altitude.meters(),5);
datafile.print(",");
//DOP
datafile.print(nmea.hdop.value(),5);
datafile.print(",");
//speed
datafile.print(nmea.speed.kmph());
datafile.print(",");
//bearing
datafile.print(nmea.course.deg());
//last field saved, start a new line
datafile.println();
#ifdef SERIALOUT
Serial.println(F("Data written."));
#endif
//close datafile
datafile.close();
} //if datafile succeeds
else { //datafile error
#ifdef SERIALOUT
Serial.println(F("Write error"));
#endif
//flash error LED
flashLED(400, R);
} //datafile
//Debug: the following closing brace moved upward to intiate logging even before a fix
//} //!nmea.location.isValid() ends
} //recentCharProcessed < 10
#ifdef SERIALOUT
Serial.print("freeMemory()=");
Serial.println(freeMemory());
#endif
}
// This custom version of delay() ensures that the nmea object is being "fed".
static void smartDelay(unsigned long ms)
{
unsigned long start = millis();
do
{
while (gps.available())
nmea.encode(gps.read());
} while (millis() - start < ms);
}
//red LED indicator of Errors
static void flashLED(int duration, byte color)
{
digitalWrite(color, LOW);
smartDelay(duration);
digitalWrite(color, HIGH);
}
static void flashLEDbicolor(int duration, byte color1, byte color2)
{
digitalWrite(color1, LOW);
digitalWrite(color2, LOW);
smartDelay(duration);
digitalWrite(color1, HIGH);
digitalWrite(color2, HIGH);
}