SD card unable to write to file

Hello,

I'm struggling to write to an SD card. I'm using an official Arduino Uno and have tested with this SD card module.
I used the right formatter and have tested with following SD cards:

  • Kingston sdhc micro SD 8GB class 4 in an adapter
  • SanDisk Ultra SDHC 8GB class 10
  • SanDisk Ultra Micro SDHC 16GB class 10 in an adapter
  • Kodak by EMTEC SDHC 8GB class 10

The code I used is the "Datalogger" example of the SD.h library that comes with arduino 1.6.10

/*
  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 <SPI.h>
#include <SD.h>

const int chipSelect = 4;

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


  Serial.print("Initializing 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.");
}

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

  // read three sensors and append to the string:
  for (int analogPin = 0; analogPin < 3; analogPin++) {
    int sensor = analogRead(analogPin);
    dataString += String(sensor);
    if (analogPin < 2) {
      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");
  }
}

When i execute this code serial looks like this:

Initializing SD card...card initialized.
error opening datalog.txt
296,304,294
303,312,304
297,303,296
296,303,297
302,310,303

This goes on.

When I run it again it looks like this:

Initializing SD card...card initialized.
402,371,333
error opening datalog.txt
error opening datalog.txt
error opening datalog.txt
error opening datalog.txt
error opening datalog.txt

When I run it again it's the same as the last one.

On the first run the file gets created. And this is visible on the SD. But the file is empty.

I have ran other tests, and i can read files (i created .txt files on the computer). But as soon as i use write or print functions reading doesn't work anymore. (file object returns false).

Sometimes when writing to the SD cards I end up with 6 or more files, in various sizes (few GBs) and from different dates with names of messed up ascii characters. I am unable to delete them and have to format the card.

I've looked up a lot of things because I thought this would be a common issue but I did not find a solution.

I have tried including pinMode (10, OUTPUT); into the setup but I think it's already included in the library. It made no difference.(found it in this post)

I've also found this post which says:

The wiring is right. I had the exact same problem. It's a voltage problem. Well, in fact it is a current problem.

You should NOT power up the SD module with 5V.
...
Writing requires much more power than creating files, deleting files, create folders, navigate into folders... That's why you're facing this issue.

But it's about a different SD card module which doesn't have a 5V pin like mine has.

I have no more ideas what to do. Is this the end? :disappointed_relieved:

Three bits of caution:

Check the maximum size that can be handled - 16Gb might be too big. I recognise that they might be hard to find but a 2Gb SD should be big enough for anybody.

You appear to be using Strings, which is not a good idea. If it doesn't give you grief immediately, it surely will eventually. It is also unnecessary. You only need to read the sensors and dataFile.print the values as you go along.

You might be simply clogging up the system as the SD routines don't have time to do their job. How often do you really need to read the sensors? Will once a second do? If so, put a delay(1000); in at the end of the loop, and see if that helps. If not, still do it and see if it helps. If it does, don't ask why a delay wasn't there in the first place, just try and adapt it to your needs. That programme looks like the example in the IDE, and I rather suspect it had some part in me having no end of grief getting my SD to work.

Thanks for the reply Nick.

All SDHC cards seem to be supported says here:

The library supports FAT16 and FAT32 file systems on standard SD cards and SDHC cards.

I realize that 2GB would be more than enough but I can't find any nearby...

I am using strings because the example is. I also tested with this program:

/*
  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 <SPI.h>
#include <SD.h>

const int chipSelect = 4;

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  pinMode(10, OUTPUT);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }


  Serial.print("Initializing 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.");
}

void loop() {
  delay(1000);


  // 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(6);
    dataFile.close();
    // print to the serial port too:
    Serial.println(6);
  }
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening datalog.txt");
  }
}

Which just writes a 6 every second. But the same results.

The only difference is that on second restart the first line isn't "error opening datalog.txt" but it's just sixes.

I have also put in a delay to adress your third suggestion but it doesn't make a difference...

Attached is also a screenshot of what the card looks like after I ran this program. While it didn't happen with the first program with this program the card always needs a format after this. Because all files are corrupted.

I have also attached a 12V dc adpater to the arduino instead of running it on USB power. Just to make sure.

Other things I could try?

I have also tried the SdFat library and especially the datalpogger example

/*
 * Simple data logger.
 */
#include <SPI.h>
#include "SdFat.h"

// SD chip select pin.  Be sure to disable any other SPI devices such as Enet.
const uint8_t chipSelect = SS;

// Interval between data records in milliseconds.
// The interval must be greater than the maximum SD write latency plus the
// time to acquire and write data to the SD to avoid overrun errors.
// Run the bench example to check the quality of your SD card.
const uint32_t SAMPLE_INTERVAL_MS = 1000;

// Log file base name.  Must be six characters or less.
#define FILE_BASE_NAME "Data"
//------------------------------------------------------------------------------
// File system object.
SdFat sd;

// Log file.
SdFile file;

// Time in micros for next data record.
uint32_t logTime;

//==============================================================================
// User functions.  Edit writeHeader() and logData() for your requirements.

const uint8_t ANALOG_COUNT = 4;
//------------------------------------------------------------------------------
// Write data header.
void writeHeader() {
  file.print(F("micros"));
  for (uint8_t i = 0; i < ANALOG_COUNT; i++) {
    file.print(F(",adc"));
    file.print(i, DEC);
  }
  file.println();
}
//------------------------------------------------------------------------------
// Log a data record.
void logData() {
  uint16_t data[ANALOG_COUNT];

  // Read all channels to avoid SD write latency between readings.
  for (uint8_t i = 0; i < ANALOG_COUNT; i++) {
    data[i] = analogRead(i);
  }
  // Write data to file.  Start with log time in micros.
  file.print(logTime);

  // Write ADC data to CSV record.
  for (uint8_t i = 0; i < ANALOG_COUNT; i++) {
    file.write(',');
    file.print(data[i]);
  }
  file.println();
}
//==============================================================================
// Error messages stored in flash.
#define error(msg) sd.errorHalt(F(msg))
//------------------------------------------------------------------------------
void setup() {
  const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
  char fileName[13] = FILE_BASE_NAME "00.csv";

  Serial.begin(9600);
  
  // Wait for USB Serial 
  while (!Serial) {
    SysCall::yield();
  }
  delay(1000);

  Serial.println(F("Type any character to start"));
  while (!Serial.available()) {
    SysCall::yield();
  }
  
  // Initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
  // breadboards.  use SPI_FULL_SPEED for better performance.
  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
    sd.initErrorHalt();
  }

  // Find an unused file name.
  if (BASE_NAME_SIZE > 6) {
    error("FILE_BASE_NAME too long");
  }
  while (sd.exists(fileName)) {
    if (fileName[BASE_NAME_SIZE + 1] != '9') {
      fileName[BASE_NAME_SIZE + 1]++;
    } else if (fileName[BASE_NAME_SIZE] != '9') {
      fileName[BASE_NAME_SIZE + 1] = '0';
      fileName[BASE_NAME_SIZE]++;
    } else {
      error("Can't create file name");
    }
  }
  if (!file.open(fileName, O_CREAT | O_WRITE | O_EXCL)) {
    error("file.open");
  }
  // Read any Serial data.
  do {
    delay(10);
  } while (Serial.available() && Serial.read() >= 0);

  Serial.print(F("Logging to: "));
  Serial.println(fileName);
  Serial.println(F("Type any character to stop"));

  // Write data header.
  writeHeader();

  // Start on a multiple of the sample interval.
  logTime = micros()/(1000UL*SAMPLE_INTERVAL_MS) + 1;
  logTime *= 1000UL*SAMPLE_INTERVAL_MS;
}
//------------------------------------------------------------------------------
void loop() {
  // Time for next record.
  logTime += 1000UL*SAMPLE_INTERVAL_MS;

  // Wait for log time.
  int32_t diff;
  do {
    diff = micros() - logTime;
  } while (diff < 0);

  // Check for data rate too high.
  if (diff > 10) {
    error("Missed data record");
  }

  logData();

  // Force data to SD and update the directory entry to avoid data loss.
  if (!file.sync() || file.getWriteError()) {
    error("write error");
  }

  if (Serial.available()) {
    // Close file and stop.
    file.close();
    Serial.println(F("Done"));
    SysCall::halt();
  }
}

This gives me following result:

error: write error
SD errorCode: 0X13,0X0

Someone knows what this means?

JensBatelier:
All SDHC cards seem to be supported says here:I realize that 2GB would be more than enough but I can't find any nearby...

That note is undated, could be old and, while valid at the time, may not be valid now. You still need to check this but I would have thought 8Gb was OK.

I am using strings because the example is. I also tested with this program:

It still isn't a good idea

/*

SD card datalogger

void loop() {
 delay(1000);



Which just writes a 6 every second. But the same results.

The only difference is that on second restart the first line isn't "error opening datalog.txt" but it's just sixes.

I have also put in a delay to address your third suggestion but it doesn't make a difference...

This makes me think there is something else going on. At least the timing is sensible. Do you see what you want to see on the serial monitor?

I have also attached a 12V dc adpater to the arduino instead of running it on USB power. Just to make sure.

Very wise, but 9v would be a better choice. I bet things get seriously hot with 12v.

The standard SD libarary should be entirely adequate for this exercise. Correspondence on this forum suggest that SDFat is more a hinderance than a help, and I have never known of a good reason to use it.

Below is the stuff that I use

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

File myFile;
const int chipSelect = 4;

void setup() {
Wire.begin();
Serial.begin(115200);
// make sure that the default chip select pin 53 is set to
// output, even if you don't use it:
pinMode(53, OUTPUT);//MEGA Uno is pin 10
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect))
{
lcd.println("Card failed");
// don't do anything more:
return;
}

Void loop() {
get time and gather numeric values

myFile = SD.open(filename, FILE_WRITE);//<<<<<<<<<<<<< OPEN
myFile.print(hour);
myFile.print(":");
myFile.print(minute);
myFile.print(":");
myFile.print(second);
myFile.print(",");

myFile.print(InTemp);
myFile.print(",");
myFile.print(OutTemp);
myFile.print(",");
myFile.print(DrainTemp);
myFile.print(",");
myFile.print(diff);
myFile.println();
myFile.close();//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>CLOSE

delay(1000);
} // loop ends here

I had a similar situation with my serial output saying:

Initializing SD card...card initialized.
402,371,333
error opening datalog.txt
error opening datalog.txt
error opening datalog.txt
error opening datalog.txt

This was a on going problem (about 10 tests).

I FIXED by disconnecting my SD card module and re-wiring.

I guess i had a poor connection to the arduino.