SD card not opening

Hi all,

I'm using UNO and an microSD card shield add-on. I can write to the card, but when I try larger programs, I'm getting error messages.

The program does fine without writing to card, but when I placed the card-writing code in, it will get into checkpoint 1**, but fails to reach at checkpoint 2 where it returns an error message.

This is where the code appears to get tripped up:

  /***** RUNNER DATA *****/
  runner_data(num_of_races, runner_num, total_runners, run_time);

  if (print_race_data == true)
  {   
    Serial.println("checkpoint 1");

    if (dataFile) 
  {  
    Serial.println("checkpoint 2");

    // function calls (these will eventually become function memebers of a math calculations class)
    print_runner_data(total_runners, num_of_races, run_time);
    copy_data(total_runners, num_of_races, run_time, sorted_run_time);
    sort_data(total_runners, num_of_races, sorted_run_time);
    average(total_runners, num_of_races, runner_num, run_time, calculations);
    minimum(runner_num, total_runners, sorted_run_time, calculations);
    maximum(runner_num, total_runners, sorted_run_time, calculations);
  
  
  // CLOSE FILE
    dataFile.close();
  }


  // if the file isn't open, pop up an error:
  else 
  {
    Serial.println("error opening datalog.txt");
  }

Full code below

/*
Input runner's time for each race.
Runner ran 4 times per week, and recorded these times.

Runner1
Runner2

Structure:
- Use 1 array to store both runners' data. Thus, 2 rows, 4 columns, so array[totalRunners_dim][numRaces_dim]

*/

/****************************************************************************************
  SD card test

  This example shows how use the utility libraries on which the'
  SD library is based in order to get info about your SD card.
  Very useful for testing a card when you're not sure whether its working or not.

  The circuit:
    SD card attached to SPI bus as follows:
 ** MOSI - pin 11 on Arduino Uno/Duemilanove/Diecimila
 ** MISO - pin 12 on Arduino Uno/Duemilanove/Diecimila
 ** CLK - pin 13 on Arduino Uno/Duemilanove/Diecimila
 ** CS - depends on your SD card shield or module.
 		Pin 4 used here for consistency with other Arduino examples
*Adafruit, test (micro)SD card
https://learn.adafruit.com/adafruit-data-logger-shield/using-the-sd-card


*Arduino Wireless SD Shield
https://docs.arduino.cc/retired/shields/arduino-wireless-sd-shield
https://docs.arduino.cc/retired/getting-started-guides/ArduinoWirelessShield
https://learn.adafruit.com/adafruit-data-logger-shield/using-the-sd-card

SD Library:  https://arduinogetstarted.com/reference/library/arduino-sd-card-library
  >> remove file:  https://arduinogetstarted.com/reference/library/arduino-sd.remove

*Library:  SD Library

- Pin 4: is CS, cannot be used otherwise
- Pins 11,12,13: used for SPI (11,12), and 13 for COMMS


***************************************************************************************/

/***** LIBRARIES *****/
#include<Arduino.h>

// SD library
#include <SPI.h>
#include <SD.h>


/***** SD Card Variables *****/
  // set up variables using the SD utility library functions:
Sd2Card card;
SdVolume volume;
SdFile root;

// change this to match your SD shield or module;
// Arduino Ethernet shield: pin 4
// Adafruit SD shields and modules: pin 10
// Sparkfun SD shield: pin 8
// MKRZero SD: SDCARD_SS_PIN
const int chipSelect = 4;

String datalog = "datalog.txt";  // string var named 'datalog' will store string chars as to be used as a txt file to write it SD card
 
// CREATE FILE
File dataFile = SD.open(datalog, FILE_WRITE);  //file name, function)


/***** GLOBAL VARIABLES *****/
int runner_num = 0;     // defines start num of which runner is running
double race_time = 0;   // for array, 2nd column, race time which gets input
const int num_of_races = 4;  // change this line for # of races, defines num of array columns
  const int numRaces_dim = num_of_races;  // don't touch, only used directly for array dimensions
const int total_runners = 2; // change this line to for # of runners, defines num of array rows
  const int totalRunners_dim = total_runners;  // don't touch, only used directly for array dimensions
const int math_calculations = 2; // changes based on number of on-board calculations to perform
double run_time[total_runners][num_of_races];   // defines a 2x4 matrix. Row 1 = runner 1 data, Row 2 = runner 2 data
double sorted_run_time[total_runners][num_of_races];  // same dimensions
double calculations[total_runners][math_calculations];  
      /* structure: m x n, where n represents n-1 number of element positions, each representing a different calculation
         basic structure: calculations[runner number] : [average][minimum][maximum][median][mode]
      */
bool print_race_data = false;




/***** FUNCTION PROTOTYPES *****/
void runner_data(int num_of_races, int runner_num, int total_runners, double run_time[][numRaces_dim]);    // enter all runner data
void print_runner_data(int total_runners, int num_of_races, double run_time[][numRaces_dim]);
void copy_data(int total_runners, int num_of_races, double run_time[][numRaces_dim], double sort_run_time[][numRaces_dim]);
void sort_data(int total_runners, int num_of_races, double sorted_run_time[][numRaces_dim]);
void average(int total_runners, int num_of_races, int runner_num, double run_time[][numRaces_dim], double calculations[][totalRunners_dim]);
void minimum(int runner_num, int total_runners, double sorted_run_time[][numRaces_dim], double calculations[][totalRunners_dim]);
void maximum(int runner_num, int total_runners, double sorted_run_time[][numRaces_dim], double calculations[][totalRunners_dim]);


/***** SETUP *****/
void setup() 
{
  Serial.begin(9600);

  /***** SD Card setup *****/
  while (!Serial) 
    {
      ; // wait for serial port to connect. Needed for native USB port only
    }


    Serial.print("\nInitializing SD card...");

    // see if the card is present and can be initialized:
    if (!SD.begin(chipSelect)) 
    {
      Serial.println("Card failed, or not present");
      // don't do anything more:
      return;
    }
    
    Serial.println("card initialized.");

    SD.remove(datalog); // delete file if existed at start of each compile, and recreates it in code which follows.



  
  /***** RUNNER DATA *****/
  runner_data(num_of_races, runner_num, total_runners, run_time);

  if (print_race_data == true)
  {   
    Serial.println("checkpoint 1");

    if (dataFile) 
  {  
    Serial.println("checkpoint 2");

    // function calls (these will eventually become function memebers of a math calculations class)
    print_runner_data(total_runners, num_of_races, run_time);
    copy_data(total_runners, num_of_races, run_time, sorted_run_time);
    sort_data(total_runners, num_of_races, sorted_run_time);
    average(total_runners, num_of_races, runner_num, run_time, calculations);
    minimum(runner_num, total_runners, sorted_run_time, calculations);
    maximum(runner_num, total_runners, sorted_run_time, calculations);
  
  
  // CLOSE FILE
    dataFile.close();
  }


  // if the file isn't open, pop up an error:
  else 
  {
    Serial.println("error opening datalog.txt");
  }


 }
}

/***** MAIN LOOP *****/
void loop() 
{

// END PROGRAM
  Serial.flush();
  exit(0);
}
/***** END OF MAIN LOOP *****/


/***** FUNCTION DEFINITIONS *****/

void runner_data(int num_of_races, int runner_num, int total_runners, double run_time[][numRaces_dim])    // enter all runner data
{
  for(runner_num; runner_num < total_runners; runner_num++)
  {
    Serial.print("***\nEnter times for runner: ");
    Serial.println(runner_num+1);
  
    int j=0; // starts val is for loop
    for(j; j < num_of_races; j++)
      {
        //Serial.println("Runner 1***");   //debug line
        Serial.print("race ");
        Serial.print(j+1);        //race number
        Serial.print(": ");
        while (Serial.available() == 0) {}  // waits until data is entered into serial monitor
        run_time[runner_num][j] = Serial.parseFloat();    //takes incoming characters and converts it to a number. This is read into the buffer altogether.  Default is data type long.
        Serial.print(run_time[runner_num][j]);
        Serial.println(" minutes");


        // print to SD card
        dataFile.print("race ");
        dataFile.print(j+1);        //race number
        dataFile.print(": ");
        dataFile.print(run_time[runner_num][j]);
        dataFile.println(" minutes");

        
        // flush input buffer, do before asking for new input.  Notes: https://forum.arduino.cc/t/serial-input-basics-updated/382007
        while (Serial.available() > 0) {Serial.read();}
      }

  // increase runner number for display and entry of new array for run times
  //runner_num++;
  //j = 0; //reset for loop for next runner's data to be entered  
  }

  Serial.println("\nAll race data has been entered.");
  Serial.println("*** Race results ***");
  print_race_data = true;  // flag changed to true to allow for calcs to begin
}


void print_runner_data(int total_runners, int num_of_races, double run_time[][numRaces_dim])
{
  int a = 0;
  int b = 0;
  // recall structure of table: run_time[total_runners][num_of_races]
    for (a; a < total_runners; a++)
    {
      Serial.print("*Runner ");
      Serial.print(a+1);
      Serial.println(" data*");

        for(b; b < num_of_races; b++)
        {
          Serial.print("   race ");
          Serial.print(b+1);
          Serial.print(": ");
          Serial.print(run_time[a][b]);  // Holds 'a' constant, so it dynamically moves to and prints at array position "b"
        }
        Serial.println("\n");
        b = 0; //reset num_of_races counter to allow for next runner's times to be displayed
    }
}


void copy_data(int total_runners, int num_of_races, double run_time[][numRaces_dim], double sorted_run_time[][numRaces_dim])
{ 

  // copy run_time array into sorting array.  This preserves raw data
  int b = 0;
  int c = 0;
  // recall structure of table: run_time[total_runners][num_of_races]
    for (c; c < total_runners; c++)
    {
       for(b; b < num_of_races; b++)
        {
          sorted_run_time[c][b] = run_time[c][b];  // Holds 'c' constant, so it dynamically moves to and copies at array position "b"
        }
        b = 0; //reset num_of_races counter to allow for next runner's times to be displayed
    }

}

void sort_data(int total_runners, int num_of_races, double sorted_run_time[][numRaces_dim])
{


  //sort data int sorted array via bubble sort.
   // structure:  double sorted_run_time[total_runners][num_of_races]


  for (int i = 0; i < total_runners; i++)  // increase row for each runner
  { 
    // loop to control # of passes
    for (int pass = 0; pass < (num_of_races - 1); pass++) 
      {
        double hold;

          //loop to control number of comparisons per pass
          for (int j = 0; j < num_of_races - 1; j++)
            {
              //compare elements, swap locations if 1st element > next element
              if (sorted_run_time[i][j] > sorted_run_time[i][j+1]) // holds runner number constant
                { 
                  hold = sorted_run_time[i][j];
                  sorted_run_time[i][j] = sorted_run_time[i][j+1];
                  sorted_run_time[i][j+1] = hold;
                }
            }

      }

  }

  // print sorted array
  // recall structure of table: run_time[total_runners][num_of_races]
  Serial.println("Print Sorted Data");

    for (int a = 0; a < total_runners; a++)
    {
      Serial.print("*Runner ");
      Serial.print(a+1);
      Serial.println(" data*");
     
        for(int b = 0; b < num_of_races; b++)
        {
          Serial.print("   race ");
          Serial.print(b+1);
          Serial.print(": ");
          Serial.print(sorted_run_time[a][b]);  // Holds 'a' constant, so it dynamically moves to and prints at array position "b"
        }
        Serial.println("\n");
       //b = 0; //reset num_of_races counter to allow for next runner's times to be displayed
    }
}

void average(int total_runners, int num_of_races, int runner_num, double run_time[][numRaces_dim], double calculations[][totalRunners_dim])
{
  
  double running_total = 0;
  double average = 0;
  runner_num = 0;  //reset to default

  for(runner_num; runner_num < total_runners; runner_num++)
    {
      for(int j = 0; j < num_of_races; j++)
      {   
        running_total = running_total + run_time[runner_num][j];  
      }       
      
      average = (running_total / num_of_races);
      running_total = 0; //reset for next loop

      //place data into calcs array
      int calc_position = 0;
      calculations[runner_num][calc_position] = average;
      Serial.print("Average run time for runner ");
      Serial.print(runner_num+1);
      Serial.print(": ");
      Serial.print(calculations[runner_num][calc_position]);
      Serial.println(" minutes"); 
      calc_position++;
  }
}

void minimum(int runner_num, int total_runners, double sorted_run_time[][numRaces_dim], double calculations[][totalRunners_dim])
{
  runner_num = 0; // reset to 0
  
  for(runner_num; runner_num < total_runners; runner_num++)
  {
    calculations[runner_num][1] = sorted_run_time[runner_num][0]; //places min time from sorted array of low to high run times into calculations array
  }

    //print calc array for run min time
    runner_num = 0; // reset to 0
    int calc_position = 1;
    Serial.println(); //skip line
    
    for (runner_num; runner_num < total_runners; runner_num++)
      {
        Serial.print("Minimum run time for runner ");
        Serial.print(runner_num+1);
        Serial.print(": ");
        Serial.print(calculations[runner_num][calc_position]);
        Serial.println(" minutes"); 
      }
}

void maximum(int runner_num, int total_runners, double sorted_run_time[][numRaces_dim], double calculations[][totalRunners_dim])
{
  runner_num = 0; // reset to 0
  
  for(runner_num; runner_num < total_runners; runner_num++)
  {
    calculations[runner_num][2] = sorted_run_time[runner_num][numRaces_dim-1]; //places max time from sorted array of low to high run times into calculations array in assigned element position for max time
  }

    //print calc array for run min time
    runner_num = 0; // reset to 0
    int calc_position = 2;
    Serial.println(); //skip line

    for (runner_num; runner_num < total_runners; runner_num++)
      {
        Serial.print("Maximum run time for runner ");
        Serial.print(runner_num+1);
        Serial.print(": ");
        Serial.print(calculations[runner_num][calc_position]);
        Serial.println(" minutes"); 
      }
}

This doesn't look right to me:

How can you open a file before you have initialised the SD card interface?

Make sure one of the examples in the SD library runs as expected on your setup, before trying your own code.

With AVR-based Arduinos like the Uno, avoid using String objects. They are never necessary, and corrupt memory, leading to program crashes.

They should check that the open call was successful, not assume it was...

:smiley_cat:

It appears easiest solution would be to move

// CREATE FILE
    File dataFile = SD.open(datalog, FILE_WRITE);  //file name, function)

to global variables, but as @ markd833 noted it has to be after card initialization. But that occurs in the Setup. How can I write 'dataFile' as a global var?

Define datafile as a global variable:

File datafile;

and initialise it with the return value of the SD.open() call after the SD.remove() call:

SD.remove(datalog)
dataFile = SD.open(datalog, FILE_WRITE);

Remember to check that the file opened ok before reading/writing it.

The OPs question relates to an UNO and an SD card module. Nothing to do with PCs, Mac's or phones.

Ah, I see it's a spam post.

It sounds to me like a memory overrun; the UNO is a bit challenged. If you have a Mega, test your code on that device.

Try using the F-macro with all print statements, maybe free up enough SRAM.
How to use F() macro in Arduino? (tutorialspoint.com)

I had to abandon program code + data-logging to SD on same UNO and resort to an ATmega328P barebones and SD adapter for logging, the UNO then did the project logic and was serially connected to 328 and SD. Worked flawlessly. Data-logging code attached based on Limor Fried's code. I did a good deal of hacking to include status LEDs.

Datalogger3.zip (79.7 KB)

Thanks for your feedback! I downloaded your code mrburnette and it appears I have a similar structure to yours with declaring File datalog as a global var. I also have

datafile = SD.open(datalog, FILE_WRITE);  //file name, function)

in a similar location within the setup as you do. But the program is still failing to get into

if (dataFile) 

location, and is giving me the error:

C:\Users\ In function 'void setup()':
C:\Users\205:9: error: 'dataFile' was not declared in this scope
     if (dataFile)
         ^~~~~~~~
C:\Users\205:9: note: suggested alternative: 'datafile'
     if (dataFile)
         ^~~~~~~~
         datafile
C:\Users\ In function 'void runner_data(int, int, int, double (*)[4])':
C:\Users\267:9: error: 'dataFile' was not declared in this scope
         dataFile.print("race ");
         ^~~~~~~~
C:\Users\267:9: note: suggested alternative: 'datafile'
         dataFile.print("race ");
         ^~~~~~~~
         datafile

exit status 1

Compilation error: 'dataFile' was not declared in this scope

I'm not understanding why my code is verified at checkpoint 1 and cannot access checkpoint 2. I know I need to add code for checking if the card is open, but I've been able to make this code work on smaller projects, this area just seems odd error message.

Full code


/***** LIBRARIES *****/
#include<Arduino.h>

// SD library
#include <SPI.h>
#include <SD.h>


/***** SD Card Variables *****/
  // set up variables using the SD utility library functions:
Sd2Card card;
SdVolume volume;
SdFile root;

// change this to match your SD shield or module;
// Arduino Ethernet shield: pin 4
// Adafruit SD shields and modules: pin 10
// Sparkfun SD shield: pin 8
// MKRZero SD: SDCARD_SS_PIN

/***** PINS *****/
#define chipSelect 4


String datalog = "datalog.txt";  // string var named 'datalog' will store string chars as to be used as a txt file to write it SD card
File datafile;
 

/***** GLOBAL VARIABLES *****/
int runner_num = 0;     // defines start num of which runner is running
double race_time = 0;   // for array, 2nd column, race time which gets input
const int num_of_races = 4;  // change this line for # of races, defines num of array columns
  const int numRaces_dim = num_of_races;  // don't touch, only used directly for array dimensions
const int total_runners = 2; // change this line to for # of runners, defines num of array rows
  const int totalRunners_dim = total_runners;  // don't touch, only used directly for array dimensions
const int math_calculations = 2; // changes based on number of on-board calculations to perform
double run_time[total_runners][num_of_races];   // defines a 2x4 matrix. Row 1 = runner 1 data, Row 2 = runner 2 data
double sorted_run_time[total_runners][num_of_races];  // same dimensions
double calculations[total_runners][math_calculations];  
      /* structure: m x n, where n represents n-1 number of element positions, each representing a different calculation
         basic structure: calculations[runner number] : [average][minimum][maximum][median][mode]
      */
bool print_race_data = false;


/***** FUNCTION PROTOTYPES *****/
void runner_data(int num_of_races, int runner_num, int total_runners, double run_time[][numRaces_dim]);    // enter all runner data
void print_runner_data(int total_runners, int num_of_races, double run_time[][numRaces_dim]);
void copy_data(int total_runners, int num_of_races, double run_time[][numRaces_dim], double sort_run_time[][numRaces_dim]);
void sort_data(int total_runners, int num_of_races, double sorted_run_time[][numRaces_dim]);
void average(int total_runners, int num_of_races, int runner_num, double run_time[][numRaces_dim], double calculations[][totalRunners_dim]);
void minimum(int runner_num, int total_runners, double sorted_run_time[][numRaces_dim], double calculations[][totalRunners_dim]);
void maximum(int runner_num, int total_runners, double sorted_run_time[][numRaces_dim], double calculations[][totalRunners_dim]);


/***** SETUP *****/
void setup() 
{
  Serial.begin(9600);

  /***** SD Card setup *****/
  while (!Serial) 
    {
      ; // wait for serial port to connect. Needed for native USB port only
    }


    Serial.print("\nInitializing SD card...");

    // see if the card is present and can be initialized:
    if (!SD.begin(chipSelect)) 
    {
      Serial.println("Card failed, or not present");
      // don't do anything more:
      return;
    }
    
    Serial.println("card initialized.");

    // CREATE FILE
      //File dataFile = SD.open(datalog, FILE_WRITE);  //file name, function)

    SD.remove(datalog); // delete file if existed at start of each compile, and recreates it in code which follows.
    datafile = SD.open(datalog, FILE_WRITE);  //file name, function)



  
  /***** RUNNER DATA *****/
  runner_data(num_of_races, runner_num, total_runners, run_time);

  if (print_race_data == true)
  {   
    Serial.println("checkpoint 1");

    if (dataFile) 
  {  
    Serial.println("checkpoint 2");

    // function calls (these will eventually become function memebers of a math calculations class)
    print_runner_data(total_runners, num_of_races, run_time);
    copy_data(total_runners, num_of_races, run_time, sorted_run_time);
    sort_data(total_runners, num_of_races, sorted_run_time);
    average(total_runners, num_of_races, runner_num, run_time, calculations);
    minimum(runner_num, total_runners, sorted_run_time, calculations);
    maximum(runner_num, total_runners, sorted_run_time, calculations);
  
  
  // CLOSE FILE
    dataFile.close();
  }


  // if the file isn't open, pop up an error:
  else 
  {
    Serial.println("error opening datalog.txt");
  }


 }
}

/***** MAIN LOOP *****/
void loop() 
{

// END PROGRAM
  Serial.flush();
  exit(0);
}
/***** END OF MAIN LOOP *****/


/***** FUNCTION DEFINITIONS *****/

void runner_data(int num_of_races, int runner_num, int total_runners, double run_time[][numRaces_dim])    // enter all runner data
{
  for(runner_num; runner_num < total_runners; runner_num++)
  {
    Serial.print("***\nEnter times for runner: ");
    Serial.println(runner_num+1);
  
    int j=0; // starts val is for loop
    for(j; j < num_of_races; j++)
      {
        //Serial.println("Runner 1***");   //debug line
        Serial.print("race ");
        Serial.print(j+1);        //race number
        Serial.print(": ");
        while (Serial.available() == 0) {}  // waits until data is entered into serial monitor
        run_time[runner_num][j] = Serial.parseFloat();    //takes incoming characters and converts it to a number. This is read into the buffer altogether.  Default is data type long.
        Serial.print(run_time[runner_num][j]);
        Serial.println(" minutes");


        // print to SD card
        dataFile.print("race ");
        dataFile.print(j+1);        //race number
        dataFile.print(": ");
        dataFile.print(run_time[runner_num][j]);
        dataFile.println(" minutes");

        
        // flush input buffer, do before asking for new input.  Notes: https://forum.arduino.cc/t/serial-input-basics-updated/382007
        while (Serial.available() > 0) {Serial.read();}
      }

  // increase runner number for display and entry of new array for run times
  //runner_num++;
  //j = 0; //reset for loop for next runner's data to be entered  
  }

  Serial.println("\nAll race data has been entered.");
  Serial.println("*** Race results ***");
  print_race_data = true;  // flag changed to true to allow for calcs to begin
}


void print_runner_data(int total_runners, int num_of_races, double run_time[][numRaces_dim])
{
  int a = 0;
  int b = 0;
  // recall structure of table: run_time[total_runners][num_of_races]
    for (a; a < total_runners; a++)
    {
      Serial.print("*Runner ");
      Serial.print(a+1);
      Serial.println(" data*");

        for(b; b < num_of_races; b++)
        {
          Serial.print("   race ");
          Serial.print(b+1);
          Serial.print(": ");
          Serial.print(run_time[a][b]);  // Holds 'a' constant, so it dynamically moves to and prints at array position "b"
        }
        Serial.println("\n");
        b = 0; //reset num_of_races counter to allow for next runner's times to be displayed
    }
}


void copy_data(int total_runners, int num_of_races, double run_time[][numRaces_dim], double sorted_run_time[][numRaces_dim])
{ 

  // copy run_time array into sorting array.  This preserves raw data
  int b = 0;
  int c = 0;
  // recall structure of table: run_time[total_runners][num_of_races]
    for (c; c < total_runners; c++)
    {
       for(b; b < num_of_races; b++)
        {
          sorted_run_time[c][b] = run_time[c][b];  // Holds 'c' constant, so it dynamically moves to and copies at array position "b"
        }
        b = 0; //reset num_of_races counter to allow for next runner's times to be displayed
    }

}

void sort_data(int total_runners, int num_of_races, double sorted_run_time[][numRaces_dim])
{


  //sort data int sorted array via bubble sort.
   // structure:  double sorted_run_time[total_runners][num_of_races]


  for (int i = 0; i < total_runners; i++)  // increase row for each runner
  { 
    // loop to control # of passes
    for (int pass = 0; pass < (num_of_races - 1); pass++) 
      {
        double hold;

          //loop to control number of comparisons per pass
          for (int j = 0; j < num_of_races - 1; j++)
            {
              //compare elements, swap locations if 1st element > next element
              if (sorted_run_time[i][j] > sorted_run_time[i][j+1]) // holds runner number constant
                { 
                  hold = sorted_run_time[i][j];
                  sorted_run_time[i][j] = sorted_run_time[i][j+1];
                  sorted_run_time[i][j+1] = hold;
                }
            }

      }

  }

  // print sorted array
  // recall structure of table: run_time[total_runners][num_of_races]
  Serial.println("Print Sorted Data");

    for (int a = 0; a < total_runners; a++)
    {
      Serial.print("*Runner ");
      Serial.print(a+1);
      Serial.println(" data*");
     
        for(int b = 0; b < num_of_races; b++)
        {
          Serial.print("   race ");
          Serial.print(b+1);
          Serial.print(": ");
          Serial.print(sorted_run_time[a][b]);  // Holds 'a' constant, so it dynamically moves to and prints at array position "b"
        }
        Serial.println("\n");
       //b = 0; //reset num_of_races counter to allow for next runner's times to be displayed
    }
}

void average(int total_runners, int num_of_races, int runner_num, double run_time[][numRaces_dim], double calculations[][totalRunners_dim])
{
  
  double running_total = 0;
  double average = 0;
  runner_num = 0;  //reset to default

  for(runner_num; runner_num < total_runners; runner_num++)
    {
      for(int j = 0; j < num_of_races; j++)
      {   
        running_total = running_total + run_time[runner_num][j];  
      }       
      
      average = (running_total / num_of_races);
      running_total = 0; //reset for next loop

      //place data into calcs array
      int calc_position = 0;
      calculations[runner_num][calc_position] = average;
      Serial.print("Average run time for runner ");
      Serial.print(runner_num+1);
      Serial.print(": ");
      Serial.print(calculations[runner_num][calc_position]);
      Serial.println(" minutes"); 
      calc_position++;
  }
}

void minimum(int runner_num, int total_runners, double sorted_run_time[][numRaces_dim], double calculations[][totalRunners_dim])
{
  runner_num = 0; // reset to 0
  
  for(runner_num; runner_num < total_runners; runner_num++)
  {
    calculations[runner_num][1] = sorted_run_time[runner_num][0]; //places min time from sorted array of low to high run times into calculations array
  }

    //print calc array for run min time
    runner_num = 0; // reset to 0
    int calc_position = 1;
    Serial.println(); //skip line
    
    for (runner_num; runner_num < total_runners; runner_num++)
      {
        Serial.print("Minimum run time for runner ");
        Serial.print(runner_num+1);
        Serial.print(": ");
        Serial.print(calculations[runner_num][calc_position]);
        Serial.println(" minutes"); 
      }
}

void maximum(int runner_num, int total_runners, double sorted_run_time[][numRaces_dim], double calculations[][totalRunners_dim])
{
  runner_num = 0; // reset to 0
  
  for(runner_num; runner_num < total_runners; runner_num++)
  {
    calculations[runner_num][2] = sorted_run_time[runner_num][numRaces_dim-1]; //places max time from sorted array of low to high run times into calculations array in assigned element position for max time
  }

    //print calc array for run min time
    runner_num = 0; // reset to 0
    int calc_position = 2;
    Serial.println(); //skip line

    for (runner_num; runner_num < total_runners; runner_num++)
      {
        Serial.print("Maximum run time for runner ");
        Serial.print(runner_num+1);
        Serial.print(": ");
        Serial.print(calculations[runner_num][calc_position]);
        Serial.println(" minutes"); 
      }
}

Did you follow:
Formatting notes | Micro SD Card Breakout Board Tutorial | Adafruit Learning System

If you bought an SD card, chances are it's already pre-formatted with a FAT filesystem. However you may have problems with how the factory formats the card, or if it's an old card it needs to be reformatted. The Arduino SD library we use supports both FAT16 and FAT32 filesystems. If you have a very small SD card, say 8-32 Megabytes you might find it is formatted FAT12 which isn't supported. You'll have to reformat these card. Either way, it's always good idea to format the card before using, even if it's new!

Of course it is, because you have not bothered to read and understand the comments explaining why.

Start with the very first reply.

datafile and dataFile are not the same variable.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.