Help with understanding SD card "Datalogger" Example & 5way switch function

For my first project with my Arduino, I'm playing around setting up this shield: CAN-BUS Shield - DEV-13262 - SparkFun Electronics with an Uno. My final goal is to make a CAN/GPS/IMU datalogger for my car. To start out I'm just trying to get SD writing working and understand how to use the library.

I loaded the "Datalogger" example from the SD library, and modified the code in 2 places to use the correct SPI CS port for the shield I have. This program writes analog inputs to the SD card. I have a 5 way joystick on A1-A5, so I modified the "for" loop to read those pins. Also I added a line in setup to delete the file that will be written to, in case it already exists. Here's the code after those modifications:

/*
  SD card datalogger
 
 This example shows how to log data from three analog sensors 
 to an SD card using the SD library.
 	
 The circuit:
 * analog sensors on analog ins 0, 1, and 2
 * SD card attached to SPI bus as follows:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 4
 
 created  24 Nov 2010
 modified 9 Apr 2012
 by Tom Igoe
 
 This example code is in the public domain.
 	 
 */

#include <SD.h>

// On the Ethernet Shield, CS is pin 4. Note that even if it's not
// used as the CS pin, the hardware CS pin (10 on most Arduino boards,
// 53 on the Mega) must be left as an output or the SD library
// functions will not work.
const int chipSelect = 9;

void setup()
{
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }


  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(9, OUTPUT);

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

void loop()
{
  // make a string for assembling the data to log:
  String dataString = "";

  // read three sensors and append to the string:
  for (int analogPin = 1; analogPin < 6; analogPin++) {
    int sensor = analogRead(analogPin);
    dataString += String(sensor);
    if (analogPin < 5) {
      dataString += ","; 
    }
  }

  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  File dataFile = SD.open("datalog.txt", FILE_WRITE);

  // if the file is available, write to it:
  if (dataFile) {
    dataFile.println(dataString);
    dataFile.close();
    // print to the serial port too:
    Serial.println(dataString);
  }  
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening datalog.txt");
  } 
}

What I expect it to do is to print "Initializing SD card...card initialized.", then every cycle print a line of 5 comma separated values. What it actually does is:

Initializing SD card...card initialized.
507,446,472,425,408

Then it hangs and doesn't print the next line. I inserted some serial prints inside the for to see where it was hanging up, and found that on the second time through the void loop, it is hanging somewhere around the point where it reads the 3rd value and tries to write it to dataString. If I print dataString within the for loop, on the 3rd iteration it prints "447,433, " for example, having blank values for the 3rd column. Then it never makes it to the SD write part of the loop since it has hung in the for loop.

See next post...

I made it work with this program:

#include <SD.h>

const int chipSelect = 9;

void setup()
{
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }


  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(9, OUTPUT);
  
  // 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.txt");
}

void loop()
{
  // make a string for assembling the data to log:
  String dataString = "";

  // read three sensors and append to the string:
    int sensor1 = analogRead(A1);
    int sensor2 = analogRead(A2);
    int sensor3 = analogRead(A3);
    int sensor4 = analogRead(4);
    int sensor5 = analogRead(5);
    dataString = String(String(sensor1) + "," + String(sensor2) + "," + String(sensor3) + "," + String(sensor4) + "," + String(sensor5));


  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  File dataFile = SD.open("datalog.txt", FILE_WRITE);

  // if the file is available, write to it:
  if (dataFile) {
    dataFile.println(dataString);
    dataFile.close();
    // print to the serial port too:
    Serial.println(dataString);
  }  
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening datalog.txt");
  } 
}

This program outputs:

Initializing SD card...card initialized.
492,433,453,408,398
439,426,439,416,403
418,417,426,416,408
411,410,416,407,407
405,404,408,409,404
...

I understand why this works, but am unclear why the original program doesn't work. Is there some kind of limit on the length of the string that was being concatenated in the first program?

Also, I was wondering about the electrical function of the 5way analog switch that is hooked up to this. Here's the schematic: http://www.sparkfun.com/datasheets/Components/Buttons/SF303GJ26-3.pdf

When I move the switch in a given direction, it pull the associated pin to zero. However it also pulls the rest of the analog pins on the switch very low (but not zero). This is fine, I was just curious as to the electrical reason why the pins are not isolated from each other.

Thanks!

In the first program you are reading 5 pins - analog pins 1 through 5, and expecting to see 6 values. Why is not clear.

There is no reason, in either sketch, to be using the String class at all. So, stop it.

Can you elaborate on the expectation of 6 values? I see a for(int analogPin = 1; analogPin < 6; analogPin++), which would give 5 values (1-5), and an if(analogPin<5), which just puts a comma after every value except the last one.

Again, I didn't write this code, and after reading a few other posts I do see where you are coming from on the usage of strings and will move away from them for my actual project.

Is this an efficient way to do it without strings? Not sure if using sprintf is still storing a string in memory... I had it output to a char array, but sprintf is intended to "write formatted data to a string"...

#include <SD.h>

const int chipSelect = 9;

void setup()
{
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }


  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(9, OUTPUT);

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

void loop()
{
  // make a int array for assembling the data to log:
  int dataArray[5];

  // read three sensors and store in char array:
  for(int i=1; i<6; i++){  
    dataArray[i-1] = analogRead(i);
  }
  // create the char array
  char csvArray[24];
  sprintf(csvArray, "%i,%i,%i,%i,%i",dataArray[0],dataArray[1],dataArray[2],dataArray[3],dataArray[4]);
  
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  File dataFile = SD.open("datalog.txt", FILE_WRITE);

  // if the file is available, write to it:
  if (dataFile) {
    dataFile.println(csvArray);
    dataFile.close();
    // print to the serial port too:
    Serial.println(csvArray);
  }  
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening datalog.txt");
  } 
}

Can you elaborate on the expectation of 6 values?

Well, sure:

What I expect it to do is to print "Initializing SD card...card initialized.", then every cycle print a line of 6 comma separated values.

Is this an efficient way to do it without strings? Not sure if using sprintf is still storing a string in memory... I had it output to a char array, but sprintf is intended to "write formatted data to a string"...

No, it is not efficient. The sensor readings are stored in memory. Then a copy of the sensor reading as part of a string is stored. Why not just write the output directly to the file without storing it and without converting it to a string?

The result of writing "Hello" to the file is no different if you use

dataFile.print("Hello");

or

dataFile.print('H');
dataFile.print('e');
dataFile.print('l');
dataFile.print('l');
dataFile.print('o');

So, using

dataFile.print(analogRead(1);
dataFile.print(',');
dataFile.print(analogRead(2);
dataFile.print(',');
dataFile.print(analogRead(3);
dataFile.print(',');
dataFile.print(analogRead(4);
dataFile.print(',');
dataFile.print(analogRead(5);

minimizes the amount of memory needed to store data.

Ok, I made a typo (in my post, not the code). From the code I presented and indicating that I am reading A1-A5 it's pretty clear what the intent is.

Anyway, this has nothing to do with the problems I had with the first program, it is failing somewhere around the 3rd analogRead, in the second iteration of the loop, and nowhere in the code is there a discrepancy between referencing 5 vs 6 channels. Your complaints about my typo of expecting 6 values is irrelevant to the question I asked.

I feel I put together a pretty complete view of the problem, but from your response here and seeing your post history, it's clear you are intent on nit-picking any small errors in lieu of actually helping.

PaulS, no need for you to contribute any more.