Here's a sketch using the Data Logging shield to log data to the SD card and display the current time on the LCD display. The sketch was tested with an Arduino Duemilanove, the 328.
It will periodically create a new log file at whatever interval you want, which is helpful for long duration logging over days or weeks. Files will have a time stamp on creation and a time stamp when last modified.
Feel free to add this as another example for the data logger on github.
Rich.
Code: Select all
/* A data logger for the Arduino
This program assumes that you want to log the date, time, and your data about
once per second. We will display the time and date continuously on
the LCD display and log the data to the SD card.
The logger uses the Fat16 library, because it is sufficient for this task
and it uses less memory than the full SdFat library, such as 12kb for Fat16
versus 16kb for SdFat. The Fat16 library won't do directories, and can only
work with upper case 8.3 file names, such as LOGXXXXX.CSV
From looking up FAT on the Internet, Microsoft defined FAT12 as a card with
less than 4k clusters, FAT16 as between 4k and 64k clusters, and FAT32 as
greater than 64k clusters. Fat16 library only works with, (drum roll...),
right, FAT16 cards. You can load the Fat16 example program called
fat16info and it will tell you if the card is FAT16, number of clusters, etc.
If needed, you can use Windows to change an SD card to Fat16, click o
n 'Computer Management', then 'Disk Management', then select the disk
partition, and format the disk. Use the 'Allocation unit size' drop
arrow box to select the smallest allocation unit size that will create
less than 64k clusters, and the largest unit size that will create more
than 4k clusters. For example, a 2GB SD card can be set up as FAT16 with an
allocation unit size of 64k.
The logging routine will:
1) write to the BANNED.CSV file every logInterval
2) sync() the file every syncInterval
3) create a new file every fileInterval.
By performing a sync() frequently, you can remove the SD card at any time,
or power off or press reset, and never lose more than a few lines of data.
Creating a new file every fileInterval, allows easier management of the
log files. Also, earlier versions of Excel don't allow more than 64k
rows, so keeping the files smaller than 64k lines, will also help
managing the files.
The timer does not use delay(), so the processor is free to perform whatever
task you need whenever you are not logging, sync()-ing, or creating a new file.
If there is an error, then the program will print a message to the serial
monitor and go into a while() loop, effectively halting the Arduino.
Assuming there isn't an actual hardware problem, halting on an error will happen
whenever you remove the SD card. To restart logging, insert the SD card
and you must press reset to reset and restart the Arduino.
TODO: add some logic to the error handling routine to automatically restart the
Arduino when the SD card is reinserted.
RB Nov 6, 2010
*/
#include <Wire.h>
#include <Fat16.h>
#include "RTClib.h"
#include <LiquidCrystal.h>
const int logInterval = 1000; // milliseconds between entries
const int syncInterval = 2000; // milliseconds between file syncs
const unsigned int fileInterval = 43200; // create file frequency
/* count each time a log is entered into each file. Must be less than 65,535
counts per file. If the logInterval is one second, and fileInterval is 43200
seconds, then 43,200 seconds / 60 sec/min / 60 min/hour = 12 hour intervals
*/
unsigned int countLogs = 0; // initialize to zero
#define ECHO_TO_SERIAL // echo data that we are logging to the serial monitor
// if you don't want to echo the data to serial, comment out the above define
#ifdef ECHO_TO_SERIAL
//#define WAIT_TO_START
/* Wait for serial input in setup(), only if serial is enabled. You don't want
to define WAIT_TO_START unless ECHO_TO_SERIAL is defined, because it would
wait forever to start if you aren't using the serial monitor.
If you want echo to serial, but not wait to start,
just comment out the above define */
#endif
// define the Real Time Clock object
RTC_DS1307 RTC;
// setup an instance of DateTime class from RTClib, note that it's static
DateTime now;
// Create the objects to talk to the SD card
SdCard card;
Fat16 file;
/* set up a file name string for the log file. Default to a csv text file,
comma separated values, easily imported into excel. The five zeros will
allow up to 100,000 different file names */
char name[] = "LOG00000.CSV";
/* create an instance of LiquidCrystal, with appropriate pins
Change the below pins to suit your circuit
LCD RS pin to digital pin 4
LCD Enable pin to digital pin 5
LCD D4 pin to digital pin 6
LCD D5 pin to digital pin 7
LCD D6 pin to digital pin 8
LCD D7 pin to digital pin 9
*/
LiquidCrystal lcd(4, 5, 6, 7, 8, 9);
void error(char *str) {
// Note that we will always write error messages to the serial monitor
Serial.print("error in: ");
Serial.println(str);
/* this next statement will start an endless loop, basically stopping all
operation upon any error. Change this behavior if you want. */
while (1);
}
void initializeSDcard(void) {
// initialize the SD card
if (!card.init()) error("card.init, card must be present");
// initialize a FAT16 volume
if (!Fat16::init(&card)) error("Fat16::init, card must be FAT16");
}
void createLogFile(void) {
// create a new file, up to 100,000 files allowed
// we will create a new file every time this routine is called
// If we are creating another file after fileInterval, then we must
// close the open file first.
if (file.isOpen()) {
file.close();
}
for (uint16_t i = 0; i < 100000; i++) {
name[3] = i/10000 + '0';
name[4] = i/1000 + '0';
name[5] = i/100 + '0';
name[6] = i/10 + '0';
name[7] = i%10 + '0';
// O_CREAT - create the file if it does not exist
// O_EXCL - fail if the file exists
// O_WRITE - open for write
if (file.open(name, O_CREAT | O_EXCL | O_WRITE)) break;
}
if (!file.isOpen()) error ("file.open. is disk full or write protected?");
Serial.print("Logging to: ");
Serial.println(name);
// clear the writeError flag
file.writeError = 0;
// fetch the time
now = RTC.now();
// set creation date time
if (!file.timestamp(T_CREATE,now.year(),now.month(),now.day(),now.hour(),
now.minute(),now.second() )) {
error("create time");
}
// set write/modification date time
if (!file.timestamp(T_WRITE,now.year(),now.month(),now.day(),now.hour(),
now.minute(),now.second() )) {
error("write time");
}
// set access date
if (!file.timestamp(T_ACCESS,now.year(),now.month(),now.day(),now.hour(),
now.minute(),now.second() )) {
error("access time");
}
// write time, etc, to the file as a header
file.println("millis,stamp,date,time");
#ifdef ECHO_TO_SERIAL
Serial.println("millis,stamp,date,time");
#endif // ECHO_TO_SERIAL
// write out the header to the file, only upon creating a new file
if (file.writeError || !file.sync()) {
// check if error writing
error("startLogFile, writing header");
}
}
void logToFile(void) {
// clear file write error
file.writeError = 0;
// log milliseconds since starting
uint32_t m = millis();
file.print(m); // milliseconds since start
file.print(", ");
#ifdef ECHO_TO_SERIAL
Serial.print(m); // milliseconds since start
Serial.print(", ");
#endif
// fetch the time
now = RTC.now();
// log the date and time to file
file.print(now.unixtime()); // seconds since 1/1/1970
file.print(", ");
file.print(now.month(), DEC);
file.print("/");
file.print(now.day(), DEC);
file.print("/");
file.print(now.year(), DEC);
file.print(", ");
file.print(now.hour(), DEC);
file.print(":");
file.print(now.minute(), DEC);
file.print(":");
file.println(now.second(), DEC);
#ifdef ECHO_TO_SERIAL
Serial.print(now.unixtime()); // seconds since 1/1/1970
Serial.print(", ");
Serial.print(now.month(), DEC);
Serial.print("/");
Serial.print(now.day(), DEC);
Serial.print("/");
Serial.print(now.year(), DEC);
Serial.print(", ");
Serial.print(now.hour(), DEC);
Serial.print(":");
Serial.print(now.minute(), DEC);
Serial.print(":");
Serial.println(now.second(), DEC);
// Serial.println('"');
#endif // ECHO_TO_SERIAL
// set write/modification date time after each file entry
if (!file.timestamp(T_WRITE,now.year(),now.month(),now.day(),now.hour(),
now.minute(),now.second() )) {
error("write time");
}
if (file.writeError) error("logToFile, writing data");
}
void syncTheFile(void) {
/* don't sync too often - requires 2048 bytes of I/O to SD card.
512 bytes of I/O if using Fat16 library */
/* blink LED to show we are syncing data to the card & updating FAT!
you could use the LED on pin 13, or whatever pin had an LED */
// digitalWrite(greenLEDpin, HIGH);
if (!file.sync()) error("syncTheFile, sync error");
// digitalWrite(greenLEDpin, LOW);
}
void showTimeOnLCD(void) {
// fetch the time
now = RTC.now();
// locate the time where you want it, allow for eight characters
lcd.setCursor(0, 0);
// if less than 10, add a leading zero
if ( now.hour() < 10) lcd.print("0");
lcd.print(now.hour(), DEC);
lcd.print(":");
// if less than 10, add a leading zero
if ( now.minute() < 10) lcd.print("0");
lcd.print(now.minute(), DEC);
lcd.print(":");
// if less than 10, add a leading zero
if ( now.second() < 10) lcd.print("0");
lcd.print(now.second(), DEC);
// locate the date where you want it, allow for ten characters
lcd.setCursor(0, 1);
// if less than 10, add a leading zero
if ( now.month() < 10) lcd.print("0");
lcd.print(now.month(), DEC);
lcd.print("/");
// if less than 10, add a leading zero
if ( now.day() < 10) lcd.print("0");
lcd.print(now.day(), DEC);
lcd.print("/");
lcd.print(now.year(), DEC);
}
void setup(void) {
Serial.begin(57600);
Serial.println();
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
lcd.print("Hello, RTC & SD card");
delay(1000);
#ifdef WAIT_TO_START
Serial.println("Type any character to start");
while (!Serial.available());
#endif // WAIT_TO_START
lcd.clear(); // clear the lcd display
// setup and start the real time clock
Wire.begin();
if (!RTC.begin()) {
error("startLogFile, RTC failed");
}
// initialize the SD card and volume
initializeSDcard();
// create a new file
createLogFile();
}
void loop(void) {
/* use modulo operator to run the loop every time millis() is a multiple of
the interval. For example, if millis() returns 500, then 500 modolo 1000
returns the remainder or 500. Any non zero number is a 'true', so we negate
that true to be false. The if becomes true only when the remainder is zero
(negate the zero/false). This will work even after millis() overflows,
although the time will be off by the remainder */
if (!(millis() % logInterval)) {
logToFile();
countLogs++;
showTimeOnLCD();
}
// wait to sync the file until you want to
if (!(millis() % syncInterval)) {
syncTheFile();
}
// keep track of how many lines have been written to a file
// after so many lines, start a new file
if(countLogs >= fileInterval){
countLogs = 0; // reset our counter to zero
createLogFile(); // create a new file
}
}