How to write serial data to sd card with timestamp?

Hello, I am trying to take this code I have that writes "LEFT" and "RIGHT" to the serial monitor every time I take a footstep, and record that onto an sd card with a timestamp (can just be millis) of when "LEFT" or "RIGHT" was written to the serial monitor. Basically I'm trying to find and record the space between each footstep onto an SD card.

I am using an Adafruit Data Logger Shield (the older model that doesn't have the 2x3 pin header) with an Arduino UNO.

Adafruit's code example only writes data to the SD card in even intervals. I need my code to only send data with a time when the "LEFT" or "RIGHT" is triggered.

Does that make sense? Sorry I am a total newbie to coding. How do I integrate writing to the sd card into the code below?

MY CODE (NOTE: I am not using the xbee functionality for this application of the code, just trying to take what the xbee would normally be sending to the other xbee and instead write it to an SD card with a time stamp):

/***********************************************************************
  Going/Staying - footstep sensor processor
  =========================================

  DESCRIPTION:
    Obtains, smoothes and analyzes analog readings from two
    footstep sensors connected to LEFT_PIN and RIGHT_PIN
    respectively. Compares smoothed readings to an automatically
    calibrated base value for significant deviations, then sends
    "trigger" commands to remote circuit via XBee serial connection.
    
  REQUIRES:
    - Arduino Fio
    - XBee Series 1 module (installed on Fio and optionally 
      configured + paired with another XBee module)
    - 2 x footstep sensors configured as voltage dividers (or not?)
    
  INSTALLATION + USAGE:
    1) Reconfigure XBee module's PAN ID to match that of other XBee 
       module(s) used by system
    2) Upload sketch to Arduino Fio (without XBee installed)
    3) Install XBee on Fio    
    4) Construct and attach two footstep sensors
      - connect to pins indicated by LEFT_PIN and RIGHT_PIN below
    5) Turn on Fio and use footstep sensors 
       - On-board LED will flash when trigger command is sent via XBee

  CALIBRATION NOTES:
    - Sketch will automatically calibrate sensors at startup by taking
      many readings from sensors (defined by CALIBRATION_LENGTH) and 
      using the final average as a base value for future comparisons.
    - Whenever left/rightAverage has deviated from their base values by
      more than their respective thresholds, a "trigger" is registered
      and sent via XBee.
    - Increase or decrease the "threshold" varaibles below to decrease 
      or increase (respectively) the sensitivity of the sensors.
      
************************************************************************/

#define  LEFT_PIN  A0      // left sensor pin
#define  RIGHT_PIN  A1     // right sesnor pin
#define  BUFFER_SIZE  10   // more readings = smoother data

int UP = 0, DOWN = 1;                                     // variable names to make states more human-readable
int leftBuffer[BUFFER_SIZE], rightBuffer[BUFFER_SIZE];    // sensor data buffers
int leftTotal = 0, rightTotal = 0;                        // totals of all sensor data
int leftAverage = 0, rightAverage = 0;                    // averages of all sensor data
int leftState = UP, rightState = UP;                      // current sensor states
int leftPreviousState = UP, rightPreviousState = UP;      // last known sensor states
boolean leftTriggered = false, rightTriggered = false;    // flags to detect triggers
int index = 0;

// Maximum values of sensors (as found in Serial Monitor tests)
int leftMaxValue = 100;
int rightMaxValue = 720;

// Amount of change in sensor readings to trigger a step
int leftThreshold = 30;
int rightThreshold = 250;

// Strings to send over XBee when step is detected (must match receiving sketch)
String leftString = "Left";
String rightString = "Right";

void setup() {
  // Establish XBee serial connection
  Serial.begin(9600);
  
  // Set up sensor input pins
  pinMode(LEFT_PIN, INPUT);
  pinMode(RIGHT_PIN, INPUT);
  
  // Initialize buffers to be empty
  for(int i=0; i<BUFFER_SIZE; i++) {
    leftBuffer[i] = 0;
    rightBuffer[i] = 0; 
  }
}

void loop() {
  // Get most recent sensor data and calculate averages
  readSensors();
  
  // Process data and look for triggers
  detectTriggers();
  
  // If either sensor is triggered...
  if(leftTriggered || rightTriggered) {
    // Send notification via XBee of which sensor was triggered
    if(leftTriggered)   Serial.println(leftString);
    if(rightTriggered)  Serial.println(rightString);
    
    // Reset trigger flags to indicate trigger has been processed
    leftTriggered = false;
    rightTriggered = false;
    
    // Flash on-board LED to indicate trigger command sent
    digitalWrite(13, HIGH);
    delay(50);
    digitalWrite(13, LOW);
  }
}

/**************************************************
  Read sensors
  - take new readings and calculate new averages
***************************************************/
void readSensors() {
  // Remove last readings
  leftTotal -= leftBuffer[index];
  rightTotal -= rightBuffer[index];
  
  // Obtain new sensor values
  leftBuffer[index] = analogRead(LEFT_PIN);
  rightBuffer[index] = analogRead(RIGHT_PIN);
  
  // Add values to totals
  leftTotal += leftBuffer[index];
  rightTotal += rightBuffer[index];
  
  // Advance to next position in array
  index++;
  
  // Wrap index when it exceeds size of buffer
  if(index >= BUFFER_SIZE)
    index = 0;
    
  // Calculate averages
  leftAverage = leftTotal / BUFFER_SIZE;
  rightAverage = rightTotal / BUFFER_SIZE;
  
  // Delay between reads for stability
  delay(1);
}

/****************************************************************************
  Detect triggers
  - compare most recent sensor averages with base value + threshold to see 
    if changed significantly
*****************************************************************************/
void detectTriggers() {
  // Left sensor -------------------------------------------------
  // Find out whether foot is up or down
  if(leftAverage <= leftMaxValue - leftThreshold)
    leftState = DOWN;
  else
    leftState = UP;
  
  // If foot was previously up and is now down, register a trigger
  if(leftState == DOWN && leftPreviousState == UP)
    leftTriggered = true;
  
  // Save current sensor state
  leftPreviousState = leftState;

  // Right sensor -------------------------------------------------
  // Find out whether foot is up or down
  if(rightAverage <= rightMaxValue - rightThreshold)
    rightState = DOWN;
  else
    rightState = UP;
  
  // If foot was previously up and is now down, register a trigger
  if(rightState == DOWN && rightPreviousState == UP)
    rightTriggered = true;
  
  // Save current sensor state
  rightPreviousState = rightState;
}

Ok, I tried to add the lines to the SD card right after I write to serial, but I think I must have missed something. Can you point it out.

A file gets created on the SD card, but the file is blank.

Here is my code:

/***********************************************************************
  Going/Staying - footstep sensor processor
  =========================================

  DESCRIPTION:
    Obtains, smoothes and analyzes analog readings from two
    footstep sensors connected to LEFT_PIN and RIGHT_PIN
    respectively. Compares smoothed readings to an automatically
    calibrated base value for significant deviations, then sends
    "trigger" commands to remote circuit via XBee serial connection.
    
  REQUIRES:
    - Arduino Fio
    - XBee Series 1 module (installed on Fio and optionally 
      configured + paired with another XBee module)
    - 2 x footstep sensors configured as voltage dividers (or not?)
    
  INSTALLATION + USAGE:
    1) Reconfigure XBee module's PAN ID to match that of other XBee 
       module(s) used by system
    2) Upload sketch to Arduino Fio (without XBee installed)
    3) Install XBee on Fio    
    4) Construct and attach two footstep sensors
      - connect to pins indicated by LEFT_PIN and RIGHT_PIN below
    5) Turn on Fio and use footstep sensors 
       - On-board LED will flash when trigger command is sent via XBee

  CALIBRATION NOTES:
    - Sketch will automatically calibrate sensors at startup by taking
      many readings from sensors (defined by CALIBRATION_LENGTH) and 
      using the final average as a base value for future comparisons.
    - Whenever left/rightAverage has deviated from their base values by
      more than their respective thresholds, a "trigger" is registered
      and sent via XBee.
    - Increase or decrease the "threshold" varaibles below to decrease 
      or increase (respectively) the sensitivity of the sensors.
      
************************************************************************/
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include "RTClib.h"

uint32_t syncTime = 0; // time of last sync()

#define  LEFT_PIN  A0      // left sensor pin
#define  RIGHT_PIN  A1     // right sesnor pin
#define  BUFFER_SIZE  10   // more readings = smoother data

#define ECHO_TO_SERIAL   1 // echo data to serial port
#define WAIT_TO_START    0 // Wait for serial input in setup()

// for the data logging shield, we use digital pin 10 for the SD cs line
const int chipSelect = 10;

// the logging file
File logfile;

int UP = 0, DOWN = 1;                                     // variable names to make states more human-readable
int leftBuffer[BUFFER_SIZE], rightBuffer[BUFFER_SIZE];    // sensor data buffers
int leftTotal = 0, rightTotal = 0;                        // totals of all sensor data
int leftAverage = 0, rightAverage = 0;                    // averages of all sensor data
int leftState = UP, rightState = UP;                      // current sensor states
int leftPreviousState = UP, rightPreviousState = UP;      // last known sensor states
boolean leftTriggered = false, rightTriggered = false;    // flags to detect triggers
int index = 0;

// Maximum values of sensors (as found in Serial Monitor tests)
int leftMaxValue = 90;
int rightMaxValue = 700;

// Amount of change in sensor readings to trigger a step
int leftThreshold = 30;
int rightThreshold = 100;

// Strings to send over XBee when step is detected (must match receiving sketch)
String leftString = "Left";
String rightString = "Right";


void error(char *str)
{
  Serial.print("error: ");
  Serial.println(str);
  


  while(1);
}

void setup() {
  // Establish XBee serial connection
  Serial.begin(9600);
  
  // Set up sensor input pins
  pinMode(LEFT_PIN, INPUT);
  pinMode(RIGHT_PIN, INPUT);
  
  // Initialize buffers to be empty
  for(int i=0; i<BUFFER_SIZE; i++) {
    leftBuffer[i] = 0;
    rightBuffer[i] = 0; 
  }
  
  // initialize the SD card
  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);
  
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    error("Card failed, or not present");
  }
  Serial.println("card initialized.");
  
  // create a new file
  char filename[] = "LOGGER00.CSV";
  for (uint8_t i = 0; i < 100; i++) {
    filename[6] = i/10 + '0';
    filename[7] = i%10 + '0';
    if (! SD.exists(filename)) {
      // only open a new file if it doesn't exist
      logfile = SD.open(filename, FILE_WRITE); 
      break;  // leave the loop!
    }
  }
    if (! logfile) {
    error("couldnt create file");
  }
 
  Serial.print("Logging to: ");
  Serial.println(filename);
  
   logfile.println("millis,left,right");    
#if ECHO_TO_SERIAL
  Serial.println("millis,left,right");
#endif //ECHO_TO_SERIAL

}

void loop() {
  // Get most recent sensor data and calculate averages
  readSensors();
  
  // Process data and look for triggers
  detectTriggers();
  
  // If either sensor is triggered...
  if(leftTriggered || rightTriggered) {
    // Send notification via XBee of which sensor was triggered
    if(leftTriggered)   Serial.println(leftString);
    if(rightTriggered)  Serial.println(rightString);
    
    // Reset trigger flags to indicate trigger has been processed
    leftTriggered = false;
    rightTriggered = false;
    
    // Flash on-board LED to indicate trigger command sent
    digitalWrite(13, HIGH);
    delay(50);
    digitalWrite(13, LOW);
    
    // log milliseconds since starting
  uint32_t m = millis();
  logfile.print(m);           // milliseconds since start
  logfile.print(", ");    
#if ECHO_TO_SERIAL
  Serial.print(m);         // milliseconds since start
  Serial.print(", ");  
#endif

   logfile.print(", ");    
  logfile.print(leftString);
  logfile.print(", ");    
  logfile.print(rightString);
  }

}

/**************************************************
  Read sensors
  - take new readings and calculate new averages
***************************************************/
void readSensors() {
  // Remove last readings
  leftTotal -= leftBuffer[index];
  rightTotal -= rightBuffer[index];
  
  // Obtain new sensor values
  leftBuffer[index] = analogRead(LEFT_PIN);
  rightBuffer[index] = analogRead(RIGHT_PIN);
  
  // Add values to totals
  leftTotal += leftBuffer[index];
  rightTotal += rightBuffer[index];
  
  // Advance to next position in array
  index++;
  
  // Wrap index when it exceeds size of buffer
  if(index >= BUFFER_SIZE)
    index = 0;
    
  // Calculate averages
  leftAverage = leftTotal / BUFFER_SIZE;
  rightAverage = rightTotal / BUFFER_SIZE;
  
  // Delay between reads for stability
  delay(1);
}

/****************************************************************************
  Detect triggers
  - compare most recent sensor averages with base value + threshold to see 
    if changed significantly
*****************************************************************************/
void detectTriggers() {
  // Left sensor -------------------------------------------------
  // Find out whether foot is up or down
  if(leftAverage <= leftMaxValue - leftThreshold)
    leftState = DOWN;
  else
    leftState = UP;
  
  // If foot was previously up and is now down, register a trigger
  if(leftState == DOWN && leftPreviousState == UP)
    leftTriggered = true;
  
  // Save current sensor state
  leftPreviousState = leftState;

  // Right sensor -------------------------------------------------
  // Find out whether foot is up or down
  if(rightAverage <= rightMaxValue - rightThreshold)
    rightState = DOWN;
  else
    rightState = UP;
  
  // If foot was previously up and is now down, register a trigger
  if(rightState == DOWN && rightPreviousState == UP)
    rightTriggered = true;
  
  // Save current sensor state
  rightPreviousState = rightState;
}

Here is what the serial monitor showed, it looks like it is pairing the milli seconds with when the footstep was taken, just not going to the SD card...

In SD card...card initialized.
Logging to: LOGGER06.CSV
millis,left,right
Left
Right
156, Initializing SD card...card initialized.
Logging to: LOGGER07.CSV
millis,left,right
Left
Right
113, Left
1464, Right
1518, Right
2623, Left
3697, Right
4696, Left
5806, Right
6825, Left
7797, Right
8691, Left
9753, Right
10646, Left
11651, Right
12571, Left
13608, Right
14568, Left
15592, Right
16545, Left
17485, Right
18349, Left

Aw well thank you for helping!

Ok it did write the SD disk now, but the format is super weird in the excel file (see screenshot attachment), how do I get the numbers to look how they do in the serial monitor? I also noticed the millis with left/right seems to be staggered in the serial monitor.. like "Left" "463" show up together (see serial monitor below) when it's live.

/***********************************************************************
  Going/Staying - footstep sensor processor
  =========================================

#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include "RTClib.h"

#define SYNC_INTERVAL 1000 // mills between calls to flush() - to write data to the card
uint32_t syncTime = 0; // time of last sync()

#define  LEFT_PIN  A0      // left sensor pin
#define  RIGHT_PIN  A1     // right sesnor pin
#define  BUFFER_SIZE  10   // more readings = smoother data

#define ECHO_TO_SERIAL   1 // echo data to serial port
#define WAIT_TO_START    0 // Wait for serial input in setup()

// for the data logging shield, we use digital pin 10 for the SD cs line
const int chipSelect = 10;

// the logging file
File logfile;

int UP = 0, DOWN = 1;                                     // variable names to make states more human-readable
int leftBuffer[BUFFER_SIZE], rightBuffer[BUFFER_SIZE];    // sensor data buffers
int leftTotal = 0, rightTotal = 0;                        // totals of all sensor data
int leftAverage = 0, rightAverage = 0;                    // averages of all sensor data
int leftState = UP, rightState = UP;                      // current sensor states
int leftPreviousState = UP, rightPreviousState = UP;      // last known sensor states
boolean leftTriggered = false, rightTriggered = false;    // flags to detect triggers
int index = 0;

// Maximum values of sensors (as found in Serial Monitor tests)
int leftMaxValue = 90;
int rightMaxValue = 700;

// Amount of change in sensor readings to trigger a step
int leftThreshold = 30;
int rightThreshold = 100;

// Strings to send over XBee when step is detected (must match receiving sketch)
String leftString = "Left";
String rightString = "Right";


void error(char *str)
{
  Serial.print("error: ");
  Serial.println(str);
  


  while(1);
}

void setup() {
  // Establish XBee serial connection
  Serial.begin(9600);
  
  // Set up sensor input pins
  pinMode(LEFT_PIN, INPUT);
  pinMode(RIGHT_PIN, INPUT);
  
  // Initialize buffers to be empty
  for(int i=0; i<BUFFER_SIZE; i++) {
    leftBuffer[i] = 0;
    rightBuffer[i] = 0; 
  }
  
  // initialize the SD card
  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);
  
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    error("Card failed, or not present");
  }
  Serial.println("card initialized.");
  
  // create a new file
  char filename[] = "LOGGER00.CSV";
  for (uint8_t i = 0; i < 100; i++) {
    filename[6] = i/10 + '0';
    filename[7] = i%10 + '0';
    if (! SD.exists(filename)) {
      // only open a new file if it doesn't exist
      logfile = SD.open(filename, FILE_WRITE); 
      break;  // leave the loop!
    }
  }
    if (! logfile) {
    error("couldnt create file");
  }
 
  Serial.print("Logging to: ");
  Serial.println(filename);
  
   logfile.println("millis,step");    
#if ECHO_TO_SERIAL
  Serial.println("millis,step");
#endif //ECHO_TO_SERIAL

}

void loop() {
  // Get most recent sensor data and calculate averages
  readSensors();
  
  // Process data and look for triggers
  detectTriggers();
  
  // If either sensor is triggered...
  if(leftTriggered || rightTriggered) {
    // Send notification via XBee of which sensor was triggered
    if(leftTriggered)   Serial.println(leftString);
    if(rightTriggered)  Serial.println(rightString);
    
    // Reset trigger flags to indicate trigger has been processed
    leftTriggered = false;
    rightTriggered = false;
    
    // Flash on-board LED to indicate trigger command sent
    digitalWrite(13, HIGH);
    delay(50);
    digitalWrite(13, LOW);
    
    // log milliseconds since starting
  uint32_t m = millis();
  logfile.print(m);           // milliseconds since start
  logfile.print(", ");    
#if ECHO_TO_SERIAL
  Serial.print(m);         // milliseconds since start
  Serial.print(", ");  
#endif

   logfile.print(", ");    
  logfile.print(leftString);
  logfile.print(", ");    
  logfile.print(rightString);
  
    // Now we write data to disk! Don't sync too often - requires 2048 bytes of I/O to SD card
  // which uses a bunch of power and takes time
  if ((millis() - syncTime) < SYNC_INTERVAL) return;
  syncTime = millis();
    logfile.flush();
  }

}

/**************************************************
  Read sensors
  - take new readings and calculate new averages
***************************************************/
void readSensors() {
  // Remove last readings
  leftTotal -= leftBuffer[index];
  rightTotal -= rightBuffer[index];
  
  // Obtain new sensor values
  leftBuffer[index] = analogRead(LEFT_PIN);
  rightBuffer[index] = analogRead(RIGHT_PIN);
  
  // Add values to totals
  leftTotal += leftBuffer[index];
  rightTotal += rightBuffer[index];
  
  // Advance to next position in array
  index++;
  
  // Wrap index when it exceeds size of buffer
  if(index >= BUFFER_SIZE)
    index = 0;
    
  // Calculate averages
  leftAverage = leftTotal / BUFFER_SIZE;
  rightAverage = rightTotal / BUFFER_SIZE;
  
  // Delay between reads for stability
  delay(1);
}

/****************************************************************************
  Detect triggers
  - compare most recent sensor averages with base value + threshold to see 
    if changed significantly
*****************************************************************************/
void detectTriggers() {
  // Left sensor -------------------------------------------------
  // Find out whether foot is up or down
  if(leftAverage <= leftMaxValue - leftThreshold)
    leftState = DOWN;
  else
    leftState = UP;
  
  // If foot was previously up and is now down, register a trigger
  if(leftState == DOWN && leftPreviousState == UP)
    leftTriggered = true;
  
  // Save current sensor state
  leftPreviousState = leftState;

  // Right sensor -------------------------------------------------
  // Find out whether foot is up or down
  if(rightAverage <= rightMaxValue - rightThreshold)
    rightState = DOWN;
  else
    rightState = UP;
  
  // If foot was previously up and is now down, register a trigger
  if(rightState == DOWN && rightPreviousState == UP)
    rightTriggered = true;
  
  // Save current sensor state
  rightPreviousState = rightState;
}

In.card initialized.
Logging to: LOGGER15.CSV
millis,step
Left
Right
166, Initializing SD card...card initialized.
Logging to: LOGGER16.CSV
millis,step
Left
Right
173, Left
463, Right
1375, Right
4154, Right
4860, Left
5278, Right
6958, Left
8076, Right
9223, Left
10305, Right
11474, Left
12424, Right
13520,

Thank you! I got it all fixed and formatted how I want it! :slight_smile: