Data logger, RTC,SD,&LCD example
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Data logger, RTC,SD,&LCD example

by RichB on Sat Nov 06, 2010 8:48 am

Hello Lady Ada,

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.


Code: Select all | TOGGLE FULL SIZE
/* 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 LOGXXXXXX.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
//#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 */

// define the Real Time Clock object

// 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: ");
  /* 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()) {
  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: ");

  // 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
#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(", ");
  Serial.print(m);         // milliseconds since start
  Serial.print(", ");

  // 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(now.day(), DEC);
  file.print(now.year(), DEC);
  file.print(", ");
  file.print(now.hour(), DEC);
  file.print(now.minute(), DEC);
  file.println(now.second(), DEC);
  Serial.print(now.unixtime());  // seconds since 1/1/1970
  Serial.print(", ");
  Serial.print(now.month(), DEC);
  Serial.print(now.day(), DEC);
  Serial.print(now.year(), DEC);
  Serial.print(", ");
  Serial.print(now.hour(), DEC);
  Serial.print(now.minute(), DEC);
  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);
  // if less than 10, add a leading zero
  if ( now.minute() < 10) lcd.print("0");
  lcd.print(now.minute(), DEC);
  // 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);
  // if less than 10, add a leading zero
  if ( now.day() < 10) lcd.print("0");
  lcd.print(now.day(), DEC);
  lcd.print(now.year(), DEC);

void setup(void) {

  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  lcd.print("Hello, RTC & SD card");

  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
  if (!RTC.begin()) {
    error("startLogFile, RTC failed");
  // initialize the SD card and volume
  // create a new file

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)) {
  // wait to sync the file until you want to
  if (!(millis() % syncInterval)) {
  // 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
Posts: 6
Joined: Tue Jul 27, 2010 2:29 pm

Re: Data logger, RTC,SD,&LCD example

by adafruit on Sat Nov 06, 2010 11:42 am


Posts: 12151
Joined: Thu Apr 06, 2006 4:21 pm
Location: nyc

Please be positive and constructive with your questions and comments.