Go Down

Topic: Problem writing to SD card (Read 1 time) previous topic - next topic

kayle

I hope this is the correct place to ask this question. I think I have a code problem, rather than a hardware problem.
I am trying to write the input of an analog pin to a .csv file, once per second.
I am using an Arduino Uno with the Wireless SD shield, writing to a 1GB micro SD card formatted to FAT16.

The problem is that it writes several files, and then just seems to stop. The number of files it writes each time is not consistent ... so it is difficult to diagnose the exact problem.

I am quite new to coding, and to the Aduino in particular so it is most likely that I have made an obvious error. I have spent many days going over it and I can not think of any other way to fix it. Perhaps a more experienced person can spot my mistake?

The code is intended to:
Create a temporary string composed of 5 readings from the analog 0 pin, each spaced 1 second apart, each separated by line feed.
(Add a timestamp to each line if the RTC is attached).
Write that string to two files; "mainfile" and another file with a dynamically assigned file name.
Reset the string to empty, and repeat.

It is taking the voltage reading of a GSR sensor which is plugged into analog 0. The project is intended to allow the Arduino to be powered from a battery and worn all day while readings are taken every second and recorded to SD card. It is writing to two files because sometimes the files seem to get corrupted if power is removed during the write process. The "mainfile" is a continuous record of the data. In case this single massive file gets corrupted, the series of backup files contain the same data spread over many files. The series of backup files are called "split file" in the code. Currently each file is composed of only one file write (but this can be adjusted), and then a new file is created, and so on. (At the moment each file consists of only 5 seconds worth of data, but that will be adjusted to about 5 mins when it is working properly). The file name for each "split file" is composed of a single letter followed by a 7 digit number, and each file is named sequentially, for example, "A0000001", "A0000002", "A0000003", etc.

It seems to be correctly writing data to the file for the first few rounds, but eventually it stops. It seems to stop most often at split file number 9 or 26.

In addition it seems to go wrong when I plug the RTC in. I DID have the RTC working correctly, but now when I plug it in, the data which is written to the file just comes out as random characters (squares, currency signs, symbols, etc.). I had no idea why it is doing that, because I can't remember changing anything to cause it to happen!

Can anyone see if there is something majorly wrong that I am doing in the code?

(I will post the code in the next post, because I have run out of characters here).

kayle

Code: [Select]

/*
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.     
*/

#include <SD.h>
#include <Wire.h>
#include "RTClib.h"
RTC_DS1307 RTC;

// External Variables ----
int LinesPerString = 5;     // LinesPerString = 10 & WritesPerFile = 30 = 1 file per 5 minutes
int WritesPerFile = 1;
boolean UseSerialPrint = true;
boolean UseErrorBlink = false;
boolean UseTimestamp = true;
boolean UseMainFile = true;
boolean UseSplitFile = true;
char FileNamePrefix = 'A';
const int chipSelect = 4;    // Must be set to 4 as default for SD card library
int led = 7;                 // pin for LED
int baud = 9600;

// Internal Variables ----
int millicount;
int RTC_OK = 0;
int SD_OK = 0;
int Serial_OK = 0;
int WritesPerFileCounter = 1;
int LinesPerStringCounter = 1;
String dataString = "";
String timestamp;
int LoopOK = 1;
char filename1 = 48;
char filename2 = 48;
char filename3 = 48;
char filename4 = 48;
char filename5 = 48;
char filename6 = 48;
char filename7 = 48;
char MainFileName[9] = { 'm','a','i','n','f','i','l','e' }; 
int sensor;
//File dataFile;
//File dataFileMain;
 
void setup() // ----------------------------------------------------------------------------------------
{
// ----

  // Initialize the digital pin as output
  if ( UseErrorBlink == true )
     {
     pinMode(led, OUTPUT);
     }
   
  // RTC stuff
  Wire.begin();
  RTC.begin();
 
  // Initialize default CS pin
  pinMode(10, OUTPUT); // make sure that the default chip select pin is set to output, even if you don't use it 
  //pinMode(chipSelect, OUTPUT);
     
  // Open serial communications
  Serial.begin(baud);
  if ( Serial )
     {
     Serial_OK = 1;
     SERIAL_PRINT("Initializing SD card...");
     }

  // Check RTC ----
  if ( RTC.isrunning() )
     {
     SERIAL_PRINT("RTC is running.");
     RTC_OK = 1;
     }
  else
     {
     SERIAL_PRINT("ERROR: RTC is NOT running!");
     }

  // checkl SD ----
  if (SD.begin(chipSelect))
     {
     SERIAL_PRINT("SD card initialized.");
     SD_OK = 1;   
     }
  else
     {
      SERIAL_PRINT("ERROR: SD Card failed, or not present");
     }

// ---- 
}
// -----------------------------------------------------------------------------------------------------

void loop() // -----------------------------------------------------------------------------------------
{
// ---- 

if ( (LoopOK == 1) /*&& (RTC_OK == 1) && (SD_OK == 1)*/ )
{

SERIAL_PRINT("LoopOK = 1");
 
filename1 = 48;
while ( filename1 <= 57 )
{
filename2 = 48;
while ( filename2 <= 57 )
{
filename3 = 48;
while ( filename3 <= 57 )
{
filename4 = 48;
while ( filename4 <= 57 )
{
filename5 = 48;
while ( filename5 <= 57 )
{
filename6 = 48;
while ( filename6 <= 57 )
{
filename7 = 48;
while ( filename7 <= 57 )
{
WritesPerFileCounter = 1;
while ( WritesPerFileCounter <= WritesPerFile )
{

  // FILENAME ----
  char Filename[9] = { FileNamePrefix,filename1, filename2, filename3, filename4, filename5, filename6, filename7 };

  // DATASTRING ----
  LinesPerStringCounter = 1;
  dataString = "";
  while ( LinesPerStringCounter <= LinesPerString )
        {
        sensor = analogRead(0);   
        if ( (UseTimestamp == true) && (RTC_OK == 1) )
           {
           timestamp = "";
           DateTime now = RTC.now();
           timestamp = String(now.year())+"."+String(now.month())+"."+String(now.day())+" "+String(now.hour())+":"+String(now.minute())+":"+String(now.second());
           dataString += "\n"+String(sensor)+"\t"+String(timestamp);
           SERIAL_PRINT(String(sensor)+" "+String(timestamp)); 
           }
        if ( (UseTimestamp == false) || (RTC_OK == 0) )
           {
           dataString += "\n"+String(sensor);
           SERIAL_PRINT(String(sensor));
           }
        delay(1000);
        LinesPerStringCounter++;
         }

  // WRITE TO SPLIT FILE ----
  if ( (UseSplitFile == true) && (SD_OK == 1) )
     {
     File dataFile = SD.open(Filename, FILE_WRITE);
     if (dataFile)
        {
        if ( dataFile.println(dataString) <= 0 )
           { SERIAL_PRINT("error writing split file"); }
        dataFile.close();
        SERIAL_PRINT(dataString);
        SERIAL_PRINT(Filename);
        } 
     else
        {
        SERIAL_PRINT("error opening split file");
        if ( UseErrorBlink == true )
          {
          ERROR_BLINK_CYCLE(5, 1000);
          digitalWrite(led, HIGH);
          }
        }
     }

  // WRITE TO MAIN FILE ----
  if ( (UseMainFile == true) && (SD_OK == 1) )
     {   
     File dataFileMain = SD.open(MainFileName, FILE_WRITE);
     if (dataFileMain)
        {
        if ( dataFileMain.println(dataString) <= 0 )
           { SERIAL_PRINT("error writing main file"); }
        dataFileMain.close();
        SERIAL_PRINT(dataString);
        SERIAL_PRINT(MainFileName);
        } 
     else
        {
        SERIAL_PRINT("error opening main file");
        if ( UseErrorBlink == true )
          {
          ERROR_BLINK_CYCLE(5, 1000);
          digitalWrite(led, HIGH);
          }
        }
     }
     
delay(1000);
WritesPerFileCounter++;
}
filename7++;
}
filename6++;
}
filename5++;
}
filename4++;
}
filename3++;
}
filename2++;
}
filename1++;
}

LoopOK = 0;
}

// ----
}

// FUNCTIONS -------------------------------------------------------------------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------------------------------------------------------------------------

// ERROR_BLINK() ---------------------------------------------------------------------------------------------------------------------------------------------------------
void ERROR_BLINK_CYCLE(int cycles, int milliseconds)
{
if ( UseErrorBlink == true )
   {
   SERIAL_PRINT("ERROR_BLINK_CYCLE() function intiated");
   int counter = 0;

   while ( counter <= cycles )
         {
         digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
         SERIAL_PRINT("on");
         delay(milliseconds);               // wait for a second
         digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
         if ( Serial_OK == 1) {Serial.println("off");}
         delay(milliseconds);               // wait for a second
         counter++;
         }
     
   digitalWrite(led, HIGH);
   SERIAL_PRINT("on");
   SERIAL_PRINT("ERROR_BLINK_CYCLE() function completed");
   }
return;   
}

// SERIAL_PRINT() -------------------------------------------------------------------------------------------------------------------------------------------------------
void SERIAL_PRINT(String printstring)
{
if ( UseSerialPrint == true )
   {
   if ( Serial_OK == 1) {Serial.println(printstring);}   
   } 
return;



el_supremo

The first thing to do is get rid of String and use C-style null terminated character arrays instead.
There is a known problem with the String library, on top of which there's only 2kB of Ram in total which is not enough to support the sort of memory usage that the String library requires.
You are also doing something really strange with nested while loops but fix the strings first.

Pete

kayle

OK great, thanks. I guess that will probably make a big impact. I'll change the strings and see what happens. Might take me a while to figure out how to do that ;-)

The nested loops are there for the file name of the "split file". According to the sd library I'm using I think the file name has to be char. I am creating the file name one character at a time. Each new file name has to be "the previous file name +1". The only way I could figure out how to do that was to increase the ASCII character value by 1 each time, for each digit, in the correct order. Each digit starts at 48 (ASCII for "0"), and goes up in 1's to 57 (ASCII for 9), then resets to 48 and the one to the left goes up by one - going from right to left. So the first file is called "A0000001", the second is called "A0000002", the third is called "A0000003", and so on until I run out of battery. Hence the nested loops. (Using while rather than for, so that it is slightly easier to see where the loops end). I'm not used to using arrays and chars to make strings. The programming language I normally use just has string, double, bool and int, with automatic implicit conversion between them. There is probably a better way to increase the file name by 1 each time!

Jimmy60

Have you tried printing out your filenames to the serial monitor to see if it's doing what you want?

Go Up