Hiccups when sampling analog readings to SD card

I am currently writing analog readings to an SD card. The aim is to sample steady at 100 Hz for approximately 20 seconds when I push a button. By using timer1 and an interrupt every 10 ms this works fine when using the serial port + CoolTerm.

When I am writing to SD card there seems to be hiccups of 20-80 ms every half second for the first 4-5 seconds of sampling. Then the sampling seems to stabilize. Can this be due to the SD-card itself or is it the code?

I have tried to limit the number of variables. Reserve memory for the string. Reformatted the SD card. Is there something I seem to miss out on? All kind if help is appreciated.

/*
  SD card datalogger for analog read

  


 */
#include <TimerOne.h>
#include <SPI.h>
#include <SD.h>

const int chipSelect = 10;


const int zpin = A3;                    // analog read

const int redPin = 4;                   // Red LED connected to digital pin 4
const int yellowPin = 2;                // Red LED connected to digital pin 2
const int greenPin = 3;                 // Green LED connected to digital pin 3
const int  buttonPin = 5;               // the pin that the pushbutton is attached to digital pin 5
const int  buzzerPin = 9;               // buzzer to digital pin 9

// Variables will change:
volatile int sensorValueAZ;
volatile unsigned long sensorTime;      //this is the variable use in the ISR to record the time when the sensor was readout.
volatile unsigned long sensorTimeStart; //To "reset" the timer of millis()
volatile byte sensorFlag;

int buttonState = 0;                    // current state of the button
int lastButtonState = 0;                // previous state of the button
String dataString = "";


void setup() 
{

  pinMode(redPin, OUTPUT);              // sets the digital pin as output
  pinMode(yellowPin, OUTPUT);           // sets the digital pin as output
  pinMode(greenPin, OUTPUT);            // sets the digital pin as output
  pinMode(buttonPin, INPUT);            // initialize the button pin as a input:

  //SD.begin(chipSelect);
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) 
  {
  digitalWrite(redPin, HIGH);         // sets the Red LED on
  return;
  }

  dataString.reserve(11);
  
  digitalWrite(greenPin, HIGH);         // sets the Green LED on
  digitalWrite(yellowPin, LOW);         // sets the Yellow LED off
  digitalWrite(redPin, LOW);            // sets the Red LED off
  delay(3000);
}


void loop()                                                         // MAIN LOOP
{
buttonState = digitalRead(buttonPin); 

if(buttonState==HIGH)                                               // Check if botton is pressed
 {
  digitalWrite(yellowPin, HIGH);                                    // sets the Yellow LED
 
  // create a new file
  char filename[] = "LOG00.CSV";
  for (uint8_t i = 0; i < 100; i++) {
    filename[3] = i/10 + '0';
    filename[4] = i%10 + '0';
    if (! SD.exists(filename)) {
      // only open a new file if it doesn't exist
      break;  // leave the loop!
    }
  }
 
 
 File dataFile = SD.open(filename, FILE_WRITE); 
 
 if (!dataFile)                                                     // if the file isn't open, turn on Red LED
      {                                                            
      digitalWrite(greenPin, LOW);                                  // sets the Green LED off
      digitalWrite(yellowPin, LOW);                                 // sets the Yellow LED off
      digitalWrite(redPin, HIGH);                                   // sets the Red LED on
      }

  delay(3000);                                                      // 3 sec Delay
  tone(buzzerPin,261,1000);                                         // 1 sec tone
  delay(2000);                                                      // 2 sec Delay

      
  Timer1.initialize(10000);                                          // initialize timer1, and set a 10 ms second period for the interrupt interval (i.e. the ISR will be called
                                                                    // every 10000 us to read out the potentiometer that simulates a sensor in this tutorial.
  Timer1.attachInterrupt(readoutSensors);                           // attaches the readoutPotentiometer() function as 'Interrupt Service Routine' (ISR) to the timer1 interrupt
                                                                    // this means that every time 5000 us have passed, the readoutPotentiometer() routine will be called.

  delay(1000);                                                      // 1 sec Delay
  digitalWrite(redPin, HIGH);                                       // sets the Red LED on while recording
  sensorTimeStart = millis();                                       // To "reset" the timing for each recording
  sensorTime = millis()-sensorTimeStart;                            // note the time
  
  
  
 
  
    while (sensorTime<20000)                                        // RECORDING LOOP for 20 sec
    {
    
 
    if (sensorFlag ==1)                                             // if there is a sensor reading...
      {
      //String dataString = "";                                     // instantiate (make) an object of the string class for assembling a text line of the datalog  
      dataString = String(sensorTime)+String(",") + String(sensorValueAZ);
      dataFile.println(dataString);
      sensorFlag = 0;                                               // reset the sensor reading flag. This prevents the loop from running until a new sensor reading comes from the ISR.
      
      
      if(! dataFile) 
      {                                                             // if the file isn't open, pop up an error:
      digitalWrite(greenPin, LOW);                                  // sets the Green LED off
      digitalWrite(yellowPin, LOW);                                 // sets the Yellow LED off
      digitalWrite(redPin, HIGH);                                   // sets the Red LED on
      break;
      }
      
      
     
      }  
    }
    void detachInterrupt();                                         // disables readoutSensors
    dataFile.close();
    digitalWrite(redPin, LOW);                                      // sets the Red LED off
    digitalWrite(yellowPin, LOW);                                   // sets the Yellow LED off
    tone(buzzerPin,261,100);                                       // buzzer tone
    delay(150);
    tone(buzzerPin,329,100);                                       // buzzer tone
    delay(150);
    tone(buzzerPin,392,100);                                       // buzzer tone
    
  }
}

void readoutSensors()                                               //this is the ISR routine that is executed everytime the timer1 interrupt is called.
{
  sensorValueAZ = analogRead(zpin);                                 //read out analog
  sensorTime = millis()-sensorTimeStart;                            //note the time
  sensorFlag = 1;                                                   //set the flag that tells the loop() that there is a new sensor value to be printed.
}

You will never be able to reliably log at 100 Hz with this program.

You will see delays for several reasons.

There will be delays while clusters are allocated to the file.

The standard for SD cards allows occasional long write latency. You may see this at any time as the card ages. This is mainly due to flash wear leveling. The card will occasionally remap virtual to physical addresses and move large amounts of data.

It is possible to log at high rates with a more sophisticated program.

Here is an example, LowLatencyLogger, that allocates a number of RAM buffers, allocates a large contiguous file, and logs in binary.

Thanks fat16lib!

Exactly what I needed. Seems to work pretty much straight out of the box for my project.