add sdcard-gps sketch
arduino mega based, GPS tracked SDS011 logging to SD card
This commit is contained in:
parent
ada3c1ea8c
commit
27d80fa7c3
1 changed files with 313 additions and 0 deletions
313
sdcard-gps/sdcard-gps.ino
Normal file
313
sdcard-gps/sdcard-gps.ino
Normal file
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
based on https://github.com/noerw/sodaqone-gpslogger/blob/master/sodaq-gpslogger.ino
|
||||
adapted for Arduino Mega + GPS + SDS011 + HDC1008 + SD Card reader
|
||||
|
||||
Copyright (c) 2017 Norwin Roosen, Felix Erdmann
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <SD.h>
|
||||
#include "TinyGPS++.h"
|
||||
#include <HDC100X.h>
|
||||
#include <SDS011-select-serial.h>
|
||||
|
||||
// change to Serial to disable log output.
|
||||
#define DEBUG_OUT Serial2
|
||||
#define SD_CHIPSELECT 4
|
||||
|
||||
#define MEASURE_INTERVAL 30000
|
||||
#define GPSIDLE_INTERVAL 300000
|
||||
|
||||
//Load sensors
|
||||
SDS011 sds(Serial);
|
||||
HDC100X HDC(0x43);
|
||||
TinyGPSPlus gps;
|
||||
|
||||
File logfile;
|
||||
String logfile_path;
|
||||
uint32_t cyclestart = 0;
|
||||
double lastLat = 0;
|
||||
double lastLng = 0;
|
||||
long gpsupdate_scheduled = 0;
|
||||
|
||||
//measurement variables
|
||||
float temperature = 0, humidity = 0, pm10 = 0, pm25 = 0;
|
||||
int error;
|
||||
|
||||
/**
|
||||
* rollover safe implementation, with an optional offset
|
||||
*/
|
||||
void adaptive_delay(uint32_t duration, uint32_t offset = 0) {
|
||||
unsigned long start = millis();
|
||||
for (;;) {
|
||||
unsigned long now = millis();
|
||||
unsigned long elapsed = now - start + offset;
|
||||
if (elapsed >= duration) return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* copied from SD example sketch
|
||||
* created 28 Mar 2011
|
||||
* by Limor Fried
|
||||
* modified 9 Apr 2012
|
||||
* by Tom Igoe
|
||||
*/
|
||||
bool check_sdcard (uint8_t chipSelect) {
|
||||
// set up variables using the SD utility library functions:
|
||||
Sd2Card card;
|
||||
SdVolume volume;
|
||||
SdFile root;
|
||||
|
||||
DEBUG_OUT.print("\nInitializing SD card...");
|
||||
|
||||
// we'll use the initialization code from the utility libraries
|
||||
// since we're just testing if the card is working!
|
||||
if (!card.init(SPI_HALF_SPEED, chipSelect)) {
|
||||
DEBUG_OUT.println("initialization failed. Things to check:");
|
||||
DEBUG_OUT.println("* is a card inserted?");
|
||||
DEBUG_OUT.println("* is your wiring correct?");
|
||||
DEBUG_OUT.println("* did you change the chipSelect pin to match your shield or module?");
|
||||
return false;
|
||||
} else {
|
||||
DEBUG_OUT.println("Wiring is correct and a card is present.");
|
||||
}
|
||||
|
||||
// print the type of card
|
||||
DEBUG_OUT.print("\tCard type: ");
|
||||
switch (card.type()) {
|
||||
case SD_CARD_TYPE_SD1:
|
||||
DEBUG_OUT.println("SD1");
|
||||
break;
|
||||
case SD_CARD_TYPE_SD2:
|
||||
DEBUG_OUT.println("SD2");
|
||||
break;
|
||||
case SD_CARD_TYPE_SDHC:
|
||||
DEBUG_OUT.println("SDHC");
|
||||
break;
|
||||
default:
|
||||
DEBUG_OUT.println("Unknown");
|
||||
}
|
||||
|
||||
// Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
|
||||
if (!volume.init(card)) {
|
||||
DEBUG_OUT.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
|
||||
return false;
|
||||
}
|
||||
|
||||
// print the type and size of the first FAT-type volume
|
||||
uint32_t volumesize;
|
||||
DEBUG_OUT.print("\tVolume type is FAT");
|
||||
DEBUG_OUT.println(volume.fatType(), DEC);
|
||||
|
||||
volumesize = volume.blocksPerCluster(); // clusters are collections of blocks
|
||||
volumesize *= volume.clusterCount(); // we'll have a lot of clusters
|
||||
volumesize *= 512; // SD card blocks are always 512 bytes
|
||||
DEBUG_OUT.print("\tVolume size (bytes): ");
|
||||
DEBUG_OUT.print(volumesize);
|
||||
DEBUG_OUT.print(" bytes (");
|
||||
volumesize /= 1024;
|
||||
volumesize /= 1024;
|
||||
DEBUG_OUT.print(volumesize);
|
||||
DEBUG_OUT.println(" MB)");
|
||||
|
||||
// TODO: show remaining bytes?
|
||||
|
||||
//SerialUSB.println("\tFiles found on the card (name, date and size in bytes): ");
|
||||
//root.openRoot(volume);
|
||||
// list all files in the card with date and size
|
||||
//root.ls(LS_R | LS_DATE | LS_SIZE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void pollGPS() {
|
||||
while (Serial3.available() > 0) {
|
||||
gps.encode(Serial3.read());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* poll until we have a valid location.
|
||||
* retries at most once, returns success state
|
||||
*/
|
||||
bool updateLocation() {
|
||||
// abort if the GPS device does not push valid data within one update cycle
|
||||
static const unsigned int timeout = 2000;
|
||||
unsigned long start = millis();
|
||||
|
||||
do {
|
||||
pollGPS();
|
||||
if (millis() - start > timeout) return false;
|
||||
} while (!gps.location.isUpdated() && !gps.location.isValid() && gps.location.age() >= 1000);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String make_logfile_path() {
|
||||
// filenames longer than 12 chars cannot be written..??
|
||||
return "datalog.csv";
|
||||
//return sodaq_gps.getDateTimeString().substring(0, 8) + ".csv";
|
||||
}
|
||||
|
||||
void write_log_to_stream(Print &stream) {
|
||||
logfile.print(gps.date.year());
|
||||
logfile.print(F("-"));
|
||||
if (gps.date.month() < 10) logfile.print(F("0"));
|
||||
logfile.print(gps.date.month());
|
||||
logfile.print(F("-"));
|
||||
if (gps.date.day() < 10) logfile.print(F("0"));
|
||||
logfile.print(gps.date.day());
|
||||
logfile.print(F("T"));
|
||||
logfile.print(gps.time.hour());
|
||||
logfile.print(F(":"));
|
||||
if (gps.time.minute() < 10) logfile.print(F("0"));
|
||||
logfile.print(gps.time.minute());
|
||||
logfile.print(F(":"));
|
||||
if (gps.time.second() < 10) logfile.print(F("0"));
|
||||
logfile.print(gps.time.second());
|
||||
logfile.print(F("Z"));
|
||||
stream.print(',');
|
||||
|
||||
stream.print(String(gps.location.lat(), 6));
|
||||
stream.print(',');
|
||||
stream.print(String(gps.location.lng(), 6));
|
||||
stream.print(',');
|
||||
|
||||
stream.print(temperature, 2);
|
||||
stream.print(',');
|
||||
stream.print(humidity, 2);
|
||||
stream.print(',');
|
||||
stream.print(pm25, 2);
|
||||
stream.print(',');
|
||||
stream.print(pm10, 2);
|
||||
stream.print(',');
|
||||
stream.println();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
// Open serial communications and wait for port to open
|
||||
DEBUG_OUT.begin(9600);
|
||||
while (!DEBUG_OUT) ;
|
||||
|
||||
DEBUG_OUT.print("Initializing sensors...");
|
||||
Serial3.begin(4800); // GPS
|
||||
Serial.begin(9600); // SDS011
|
||||
Wire.begin();
|
||||
HDC.begin(HDC100X_TEMP_HUMI, HDC100X_14BIT, HDC100X_14BIT, DISABLE);
|
||||
temperature = HDC.getTemp();
|
||||
DEBUG_OUT.println("done!");
|
||||
|
||||
if (!check_sdcard(SD_CHIPSELECT)) {
|
||||
while (true) ;
|
||||
}
|
||||
|
||||
// sd card
|
||||
//pinMode(SD_CHIPSELECT, INPUT);
|
||||
//digitalWrite(SD_CHIPSELECT, HIGH);
|
||||
|
||||
if (!SD.begin(SD_CHIPSELECT)) {
|
||||
DEBUG_OUT.println("can't open SD card, though a card was found...");
|
||||
while (true) ;
|
||||
}
|
||||
|
||||
DEBUG_OUT.print("starting GPS...");
|
||||
while (!updateLocation()) ; // get first fix & keep GPS enabled afterwards
|
||||
lastLat = gps.location.lat();
|
||||
lastLng = gps.location.lng();
|
||||
|
||||
logfile_path = make_logfile_path();
|
||||
|
||||
DEBUG_OUT.print("got fix. ready to write data to ");
|
||||
DEBUG_OUT.println(logfile_path);
|
||||
|
||||
// if the logfile does not exist yet, write the csv header
|
||||
if (!SD.exists(logfile_path)) {
|
||||
logfile = SD.open(logfile_path, FILE_WRITE);
|
||||
if (logfile) {
|
||||
logfile.println("timestamp,latitude,longitude,temperature,humidity,pm2.5,pm10");
|
||||
logfile.close();
|
||||
} else {
|
||||
DEBUG_OUT.print("unable to write CSV header to logfile");
|
||||
while (true) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
cyclestart = millis();
|
||||
|
||||
// TODO: only update fix, if accelerometer indicates movement?
|
||||
// TODO: check battery voltage & blink LED if low?
|
||||
if (millis() >= gpsupdate_scheduled) {
|
||||
if (!updateLocation()) {
|
||||
return DEBUG_OUT.println("couldnt get fix");
|
||||
}
|
||||
|
||||
// determine, how far we moved. if less than 20m, reduce update interval
|
||||
if (gps.distanceBetween(lastLat, lastLng, gps.location.lat(), gps.location.lng()) > 20) {
|
||||
gpsupdate_scheduled = cyclestart + MEASURE_INTERVAL;
|
||||
} else {
|
||||
gpsupdate_scheduled = cyclestart + GPSIDLE_INTERVAL;
|
||||
//digitalWrite(GPS_ENABLE, LOW); // save energy for longer interval
|
||||
}
|
||||
|
||||
lastLat = gps.location.lat();
|
||||
lastLng = gps.location.lng();
|
||||
}
|
||||
|
||||
|
||||
//-----Temperature-----//
|
||||
temperature = HDC.getTemp();
|
||||
delay(100);
|
||||
|
||||
//-----Humidity-----//
|
||||
humidity = HDC.getHumi();
|
||||
delay(100);
|
||||
|
||||
//-----fine dust-----/
|
||||
error = sds.read(&pm25, &pm10);
|
||||
if (error) {
|
||||
return DEBUG_OUT.println("could not read SDS011...");
|
||||
}
|
||||
|
||||
write_log_to_stream(DEBUG_OUT);
|
||||
|
||||
// if the file opened okay write GPS fix to it
|
||||
logfile = SD.open(logfile_path, FILE_WRITE);
|
||||
if (logfile) {
|
||||
DEBUG_OUT.print("writing to logfile ");
|
||||
DEBUG_OUT.print(logfile_path);
|
||||
DEBUG_OUT.print(" ... ");
|
||||
|
||||
write_log_to_stream(logfile);
|
||||
|
||||
logfile.close();
|
||||
DEBUG_OUT.println("done.");
|
||||
} else {
|
||||
// FIXME: this will never be evaluated, even if the card is removed??
|
||||
DEBUG_OUT.println("error opening logfile");
|
||||
}
|
||||
|
||||
// adaptive & rollover safe delay, to ensure longrunning & fixed interval
|
||||
adaptive_delay(MEASURE_INTERVAL, millis() - cyclestart);
|
||||
//delay(MEASURE_INTERVAL);
|
||||
}
|
||||
|
Loading…
Add table
Reference in a new issue