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?
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?
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.
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.
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.
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.
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;
}
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.