Good way to detect that a SD card has been removed

I generally get along pretty well with SDFat. An Arduino based controller needs to know that the SD card that it has been writing to has been removed so that it can then attempt to reconnect when one is replaced.

The cards need to be changed out occasionally to harvest the data. No problem of course, if the controller is re-set, but unnecessary starts and stops of the pump it controls are to be avoided.

It seems like the return value of sync() would work but in my tests it always returns a 1, card in or card out. Perhaps from sync()'s point of reference, the buffer of characters went somewhere. Write puts it in the buffer and does not seem like a reliable source of information either.

Any thoughts for detecting the failed writes that do not involve a bunch of unnecessary writing and reading?

Thanks in advance

Many SD sockets have Card Detect pins - is your arrangement so equipped?

Sync() may not need to access the card so it will return success.

Search the forum for card removal, there are many posts.

The only sure way is use the card detect switch on the SD socket. You need to insure all files open for write are closed before the card is removed.

All the pins have been consumed or are planned for other functions. It would be really nice to have a software fix.

I just discovered a new release of SdFat and will explore it this evening to see if there are new options.

Hurray for rename!

No hope in a new version of SdFat. I am the author. You can't tell from the SPI bus.

People try some file a operation and check for an error but it is not reliable. SdFat may get info from the cache and not return an error.

All the pins have been consumed or are planned for other functions. It would be really nice to have a software fix.

Analog pins can be used as digital pins and there exist I2C multiplexers like - Arduino Shield List: macetech Centipede Shield -

fat16lib

I wish I had your knowledge and abilities. I am grateful that you share. I cannot tell an oil well pumper that he must close all the files before changing cards and expect that it will have any effect on the way he does his work. I am just pleased that they sometimes bring me a card with data on it. They dislike resetting the controller because it shuts the pump down and The controller goes through the whole range of experiments to determine the correct pumping rate again. It is disruptive. Some of the parameters are stored in EEPROM but they change often and the controller must do little experiments all the time to keep operation optimized. It is best to not start over. The power company does that for us often enough.

What can be damaged by just pulling the card? We have done it hundreds of times and to the best of my knowledge nothing irreversible has occured.

What is the proper way to shut down a card? Just close()? What are the challenges to opening a file after the card that was pulled out is replaced?

Thanks

jc

How about putting the SD card set-up in the loop and set a flag for initializing it. The flag will have to be set in the definitions to start the card from a reset. During normal operation holding a button down stops SD card. Then holding down the button again re-initializes the card. Maybe have a displayed output to know if the card is running or not.

If you already have a button or two, you can make them multipurpose.

jcarrr,

Just pulling the card may work for you. You may lose some data but you can limit that by calling sync() in SdFat or flush() in SD.h. Call it every few seconds of after writing some key data.

A call to sync()/flush() forces all data to the SD and updates to file's directory entry. This is what close() does.

When you pull a card, all data since the last sync() will be lost, even if it was written to the card, since the directory entry has the filesize and is only updated by sync()/flush() or close().

File system error-checking tools may find problems. Space that was allocated after the last sync() call will be lost. Error-checking tools will put these allocation units in a lost/found directory.

I would copy all data from a card that has been pulled during operation and reformat it using the SD association's format tool https://www.sdcard.org/consumers/formatter_3/ before using it again.

fat16lib

If we loose a little data it makes no difference. The trends and shape of the curves after the data is subject to some noise removal is the real information. The cards have been pulled hundreds or thousands of times in use and during development.

I thought that the detecting-card-absent problem was a new problem for which we once had a solution.

After digging through old code for a different controller I found:

if (file.writeError) error("write fail");

pulling the card results in:

error: write fail
SD error: F,0

echoed to the serial monitor

Is this something of yours or some one else's that I shamelessly plagiarized?

Assuming the missing card is noted, how does one get rid of program links and knowledge of the original SD and files so a replaced SD can be tested for and utilized? It seems like there is opportunity for a lot of memory consumption if it is not released.

Thanks

JC

I have been making mods to SdFat to improve the ability to change SD cards so be sure to use the latest version of SdFat. Some changes may only be in the 20110917 beta version Google Code Archive - Long-term storage for Google Code Project Hosting..

Memory loss will not be a problem. SdFat uses no memory from the heap - no calls to malloc/free. Part of my religion about embedded systems.

Before initializing the new SD, close any files. close() will return fail but will set the state of the file closed. SdFat, like most filesystems, does not allow open of an already open file object. This tends to catch missing close bugs.

Clear writeError for files that were open for write

  file.writeError = false;

Now initialize the SD card just like you did before the error.

If you have problems let me know. Many people will be using multiple cards. SdFat now supports multiple card sockets so people want to change cards so I need to improve this capability.

For best results, the card detect switch is required but you may do O.K. without it.

fat16lib

I kludged up a test to try re-initializing the board. The functions in the code initialize the card nicely with a fresh reset but the code emits

Program Starting
card initialized
volume initialized
error: openRoot failed
Root directory opened

During the second attempt after reinserting the card. Clearly "Root directory opened " is an artifact of my sloth, the error handling routine has been short circuited to keep going with out logging, Start_up_SD() and fopen() are the same code called at startup.

Interestingly, if the card is not in place for the boot up, the controller notes this and continues with the controlling functions. If the card is inserted after the initial bootup failure to initialize, it will connect just fine. Not the next time though. I think we are close

void Start_up_SD()
{
file.close(); // just in case it is a re-open
file.writeError = false;
PgmPrintln("Program Starting");// not really
if (!card.init(SPI_FULL_SPEED)) error("card.init failed");
PgmPrint("card initialized \n");
if (!volume.init(&card)) error("volume.init failed");
PgmPrint("volume initialized \n");
if (!root.openRoot(&volume)) error("openRoot failed");
PgmPrint("Root directory opened \n");// maybe not
card_yanked = false;
return;
}

boolean fopen()
{
if (file.open(&root, file_name, O_CREAT | O_APPEND | O_WRITE))
{
PgmPrintln("data file opened");
Serial.println(file_name);
return true;
}
else return false;
}

Thanks again for the thoughtful consideration of my problem

JC

You need to close root before opening it again. Root is just another file.

fat16lib

how does one close Root?

JC

  root.close();

fat16lib

Well, that is straightforward, and it works perfectly.

I tried a lot of combinations, but I think I was stuck on Root, not root.

Thanks

Until the last version of SdFat I used either the Arduino published version of the interface or the MC version with perfect results while programming either UNOs or Duemilanoves. Since the last version of SdFat the MC version has been unusable, this is typical output:

C \__arduino-0022MCa\hardware\tools\avr\bin\avr-g++    -c  -g  -Os  -w  -fno-exceptions  -ffunction-sections  -fdata-sections  -mmcu=atmega328p  -DF_CPU=16000000L  -DARDUINO=22  -DARDUINO=     -IC \___a_projects\Pump Controller\A9   -IC \__arduino-0022MCa\hardware\arduino\cores\arduino   -IC \___a_projects\libraries\SdFat   -IC \__arduino-0022MCa\.\libraries\Wire   -IC \___a_projects\libraries\RTClib   -IC \__arduino-0022MCa\.\libraries\EEPROM    c \___a_output\A9.cpp  -o  c \___a_output\A9.cpp.o
In file included from C:\___a_projects\libraries\SdFat/SdFile.h:24,
                 from C:\___a_projects\libraries\SdFat/SdFat.h:26,
                 from A9.cpp:30:
C:\___a_projects\libraries\SdFat/SdBaseFile.h:27:13: error: operator '<' has no left operand
C:\___a_projects\libraries\SdFat/SdBaseFile.h:30:21: error: Arduino.h: No such file or directory
In file included from A9.cpp:31:
C:\___a_projects\libraries\SdFat/SdFatUtil.h:27:13: error: operator '<' has no left operand
In file included from C:\___a_projects\libraries\SdFat/SdFile.h:24,
                 from C:\___a_projects\libraries\SdFat/SdFat.h:26,
                 from A9.cpp:30:
C:\___a_projects\libraries\SdFat/SdBaseFile.h:271: error: 'Print' has not been declared
C:\___a_projects\libraries\SdFat/SdBaseFile.h:285: error: 'Print' has not been declared
C:\___a_projects\libraries\SdFat/SdBaseFile.h:287: error: 'Print' has not been declared
C:\___a_projects\libraries\SdFat/SdBaseFile.h:360: error: 'Print' has not been declared
C:\___a_projects\libraries\SdFat/SdBaseFile.h:370: error: 'Print' has not been declared
In file included from C:\___a_projects\libraries\SdFat/SdFat.h:26,
                 from A9.cpp:30:
C:\___a_projects\libraries\SdFat/SdFile.h:32: error: expected class-name before '{' token
In file included from C:\___a_projects\libraries\SdFat/ArduinoStream.h:26,
                 from C:\___a_projects\libraries\SdFat/SdFat.h:28,
                 from A9.cpp:30:
C:\___a_projects\libraries\SdFat/bufstream.h: In member function 'void ibufstream::init(const char*)':
C:\___a_projects\libraries\SdFat/bufstream.h:49: error: 'strlen' was not declared in this scope
In file included from C:\___a_projects\libraries\SdFat/SdFat.h:28,
                 from A9.cpp:30:
C:\___a_projects\libraries\SdFat/ArduinoStream.h: At global scope:
C:\___a_projects\libraries\SdFat/ArduinoStream.h:40: error: expected `)' before '&' token
C:\___a_projects\libraries\SdFat/ArduinoStream.h:82: error: ISO C++ forbids declaration of 'Stream' with no type
C:\___a_projects\libraries\SdFat/ArduinoStream.h:82: error: expected ';' before '*' token
C:\___a_projects\libraries\SdFat/ArduinoStream.h: In member function 'void ArduinoInStream::readline()':
C:\___a_projects\libraries\SdFat/ArduinoStream.h:50: error: 'hw_' was not declared in this scope
C:\___a_projects\libraries\SdFat/ArduinoStream.h:53: error: 'millis' was not declared in this scope
C:\___a_projects\libraries\SdFat/ArduinoStream.h:54: error: 'hw_' was not declared in this scope
C:\___a_projects\libraries\SdFat/ArduinoStream.h:61: error: 'hw_' was not declared in this scope
C:\___a_projects\libraries\SdFat/ArduinoStream.h: At global scope:
C:\___a_projects\libraries\SdFat/ArduinoStream.h:95: error: expected `)' before '&' token
C:\___a_projects\libraries\SdFat/ArduinoStream.h:113: error: ISO C++ forbids declaration of 'Print' with no type
C:\___a_projects\libraries\SdFat/ArduinoStream.h:113: error: expected ';' before '*' token
C:\___a_projects\libraries\SdFat/ArduinoStream.h: In member function 'virtual void ArduinoOutStream::putch(char)':
C:\___a_projects\libraries\SdFat/ArduinoStream.h:103: error: 'pr_' was not declared in this scope
C:\___a_projects\libraries\SdFat/ArduinoStream.h:104: error: 'pr_' was not declared in this scope
In file included from A9.cpp:30:
C:\___a_projects\libraries\SdFat/SdFat.h: At global scope:
C:\___a_projects\libraries\SdFat/SdFat.h:61: error: 'Print' has not been declared
In file included from A9.cpp:31:
C:\___a_projects\libraries\SdFat/SdFatUtil.h:39: error: variable or field 'print_P' declared void
C:\___a_projects\libraries\SdFat/SdFatUtil.h:39: error: 'Print' was not declared in this scope
C:\___a_projects\libraries\SdFat/SdFatUtil.h:39: error: 'pr' was not declared in this scope
C:\___a_projects\libraries\SdFat/SdFatUtil.h:39: error: expected primary-expression before 'const'
C:\___a_projects\libraries\SdFat/SdFatUtil.h:40: error: variable or field 'println_P' declared void
C:\___a_projects\libraries\SdFat/SdFatUtil.h:40: error: 'Print' was not declared in this scope
C:\___a_projects\libraries\SdFat/SdFatUtil.h:40: error: 'pr' was not declared in this scope
C:\___a_projects\libraries\SdFat/SdFatUtil.h:40: error: expected primary-expression before 'const'
A9.cpp: In function 'void loop()':
A9:148: error: 'class SdFile' has no member named 'print'
A9.cpp: In function 'void file_head_stamp()':
file_functions:91: error: 'class SdFile' has no member named 'println'
A9.cpp: In function 'void file_setup_stamp()':
file_functions:100: error: 'class SdFile' has no member named 'print'
file_functions:104: error: 'class SdFile' has no member named 'print'
file_functions:107: error: 'class SdFile' has no member named 'print'
file_functions:110: error: 'class SdFile' has no member named 'println'
file_functions:113: error: 'class SdFile' has no member named 'print'
file_functions:118: error: 'class SdFile' has no member named 'println'
file_functions:123: error: 'class SdFile' has no member named 'println'

cannot find a general discussion of the problem. Is it just my installation or is this a problem for others?

Thanks for the help. The cards can now be pulled and inserted at any time.

John C

Looks like the "MC version" does not define the ARDUINO macro. I have been using it to make mods to support both 0022 and the new Arduino 1.0.

This appears to fail in the MC version:

#if ARDUINO < 100
#include <WProgram.h>
#else  // ARDUINO
#include <Arduino.h>
#endif  // ARDUINO