Skip to content

Instantly share code, notes, and snippets.

@joeycastillo
Created October 1, 2019 00:05
Show Gist options
  • Save joeycastillo/3e39f10166f27569a9e281666c9de794 to your computer and use it in GitHub Desktop.
Save joeycastillo/3e39f10166f27569a9e281666c9de794 to your computer and use it in GitHub Desktop.
// Hiking Log
//
// The main LED is used to communicate status. Under normal operation, when board is reset, the following sequence of blinks will indicate normal operation:
// 1. Three 0.5 second pulses indicate that a card is present.
// 2. A single one-second pulse indicates that commands have been sent to the GPS module.
// 3. The LED will turn off. No more pulses will be emitted unless there is an error condition.
//
// During normal operation, the device appends a single line to the LOG.CSV file every minute, on the minute.
// You may safely remove the SD card anytime other than the one-minute mark, as that's the only time the file is open.
//
// ERROR CONDITIONS:
// 1. A rapid flashing (0.05 seconds repeatedly) means that the program was compiled with GPSECHO set to TRUE and is waiting for a serial connection. Connect a serial monitor or recompile with GPSECHO set to false.
// 2. A slightly less rapid flashing (0.1 seconds repeatedly) means that the SD card was not present. Insert an SD card and reset the device.
// 3. During the course of operation, three 0.1 second pulses indicates that an attempt to open the log file for writing failed. This is most commonly the result of the card being removed.
#include <Adafruit_GPS.h>
#include <SD.h>
#include <DHT.h>
// SD Card
#define CHIPSELECT 4
// Temperature and Humidity Sensor
#define DHTPIN 19
#define DHTTYPE DHT22
// For battery voltage logging
#define VBATPIN A9
// Connect to the GPS on the hardware port
#define GPSSerial Serial1
Adafruit_GPS GPS(&GPSSerial);
// Initialize DHT sensor.
DHT Dht(DHTPIN, DHTTYPE);
// Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console
// Set to 'true' if you want to debug and listen to the raw GPS sentences
#define GPSECHO false
// Experimental. Decreases the frequency of updates but seems to make it impossible to get a fix.
#define PMTK_SET_NMEA_UPDATE_10SEC "$PMTK220,10000*2F"
// Used in the loop to make sure we only log once per minute.
bool shouldLog = true;
// Buffer, used in formatting the lat/lon floats.
static char outstr[15];
// Utility function, turns the built-in LED on for the specified number of milliseconds, and then off for the same amount.
void blink(int period)
{
digitalWrite(LED_BUILTIN, HIGH);
delay(period);
digitalWrite(LED_BUILTIN, LOW);
delay(period);
}
void setup()
{
// General setup for communicating with the user.
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
// If GPSECHO is set to true, prevent the program from running when disconnected from serial, and only let it proceed once the serial monitor is open.
// Note: this means that if GPSECHO is true, the program will not run unless plugged in to a computer.
if (GPSECHO)
{
while (!Serial)
{
blink(50);
}
}
Serial.print("Initializing SD card...");
if (!SD.begin(CHIPSELECT))
{
Serial.println("Card failed, or not present");
while (1)
{
blink(100);
}
}
Serial.println("card initialized.");
blink(500);
blink(500);
blink(500);
Serial.print("Initializing GPS module...");
// 9600 NMEA is the default baud rate for Adafruit MTK GPS's- some use 4800
GPS.begin(9600);
// uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) including altitude
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
// uncomment this line to turn on only the "minimum recommended" data
//GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY);
// For parsing data, we don't suggest using anything but either RMC only or RMC+GGA since
// the parser doesn't care about other sentences at this time
// Set the update rate
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate
// For the parsing code to work nicely and have time to sort thru the data, and
// print it out we don't suggest using anything higher than 1 Hz
delay(1200);
// Request updates or no updates on antenna status
// GPS.sendCommand(PGCMD_ANTENNA);
GPS.sendCommand(PGCMD_NOANTENNA);
//turn on sbas
GPS.sendCommand("$PMTK313,1*2E\r\n");
//sbas mode to waas
GPS.sendCommand("$PMTK301,2*2E\r\n");
blink(1000);
Serial.println("GPS module ready.");
}
void loop() // run over and over again
{
/* GPS PARSING */
// read data from the GPS in the 'main loop'
char c = GPS.read();
// if you want to debug, this is a good time to do it!
if (GPSECHO)
{
if (c)
{
Serial.print(c);
}
}
// if a sentence is received, we can check the checksum, parse it...
if (GPS.newNMEAreceived())
{
if (!GPS.parse(GPS.lastNMEA())) // this also sets the newNMEAreceived() flag to false
{
return; // we can fail to parse a sentence in which case we should just wait for another
}
}
/* LOGGING */
int seconds = GPS.seconds;
// Log data every minute, provided we have a valid timestamp
if (shouldLog && seconds == 0 && GPS.year != 0)
{
shouldLog = false;
// make a string for assembling the data to log:
String dataString = "";
dataString += "20";
dataString += (int)GPS.year;
if (GPS.month < 10)
{
dataString += '0';
}
dataString += (int)GPS.month;
if (GPS.day < 10)
{
dataString += '0';
}
dataString += (int)GPS.day;
if (GPS.hour < 10)
{
dataString += '0';
}
dataString += (int)GPS.hour;
if (GPS.minute < 10)
{
dataString += '0';
}
dataString += (int)GPS.minute;
dataString += "00"; // we know seconds is 00, that's why we're here.
dataString += ',';
dataString += (int)GPS.fix;
dataString += ',';
dataString += (int)GPS.fixquality;
dataString += ',';
if (GPS.fix) {
dtostrf(GPS.latitudeDegrees, 7, 7, outstr);
dataString += outstr;
dataString += ',';
dtostrf(GPS.longitudeDegrees, 7, 7, outstr);
dataString += outstr;
dataString += ',';
dataString += GPS.HDOP;
dataString += ',';
dataString += GPS.speed * 1.15078;
dataString += ',';
dataString += GPS.angle;
dataString += ',';
dataString += GPS.altitude;
dataString += ',';
}
else
{
dataString += ",,,,,,";
}
dataString += (int)GPS.satellites;
dataString += ',';
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
float h = Dht.readHumidity();
// Read temperature as Celsius (the default)
float t = Dht.readTemperature();
// Read temperature as Fahrenheit (isFahrenheit = true)
float f = Dht.readTemperature(true);
if (isnan(h) || isnan(t) || isnan(f))
{
dataString += ",,,,,";
}
else
{
// Compute heat index in Fahrenheit (the default)
float hif = Dht.computeHeatIndex(f, h);
// Compute heat index in Celsius (isFahreheit = false)
float hic = Dht.computeHeatIndex(t, h, false);
dataString += h;
dataString += ',';
dataString += t;
dataString += ',';
dataString += hic;
dataString += ',';
dataString += f;
dataString += ',';
dataString += hif;
dataString += ',';
}
float measuredvbat = analogRead(VBATPIN);
measuredvbat *= 2; // we divided by 2, so multiply back
measuredvbat *= 3.3; // Multiply by 3.3V, our reference voltage
measuredvbat /= 1024; // convert to voltage
dataString += (measuredvbat);
// Print logged data to serial...
Serial.println(dataString);
// and log it to the SD card.
File dataFile = SD.open("log.csv", FILE_WRITE);
if (dataFile)
{
if (dataFile.size() == 0)
{
const String header = "Timestamp,GPS Fix,Fix Quality,Latitude,Longitude,HDOP,Speed,Angle,Altitude,Satellites In View,Relative Humidity,Temperature (C),Heat Index (C),Temperature (F),Heat Index (F),Battery Voltage";
dataFile.println(header);
}
dataFile.println(dataString);
dataFile.close();
}
else
{
Serial.println("error opening log.csv");
blink(100);
blink(100);
blink(100);
}
}
else if (seconds == 1)
{
// at the one-second mark, set shouldLog to true so we log on the next match with 0 seconds.
shouldLog = true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment