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");
}
}