I'm using a Seeed Studio SD card shield for the Arduino Uno. I'm trying to enter the filename of an existing file on the card so I can open and read its contents. I type in the filename using the input box of the serial monitor. I then read it with Serial.read and place it in a string variable. I then try to open the file using the string variable. This does not work. I get an error stating the String& is not in the SD.h file and it recommends using const char*. When I type the string literal of the filename in my Arduino sketch it opens perfectly. Does anyone know of a way to enter a filename into the input box of the serial monitor and have it open the file that I entered? Does the SD card library even allow this?
Where is your code?
Does the SD card library even allow this?
Of course you can pass variables as file names.
This is the part of my sketch that I'm having trouble with.
// String J containg the filename I entered from the serial monitor
// which contaings XYTABLE.TXT which is the file I'm trying to open.
void readFile() {
File dataFile = SD.open(J);
if (dataFile) {
while (dataFile.available()) {
Serial.write(dataFile.read());
}
dataFile.close(); Serial.println();
}
else {
Serial.print("error opening "); Serial.println(J);
}}
This doesn't work. But if I change SD.open(J) to SD.open("XYTABLE.TXT")
it works fine. The SD.open command doesn't seem to accept a string variable.
Is there a way to make it accept string variables?
You seem to be a bit secretive with your code, and it is a fair bet that the problem lies in the bit you are secretive about.
I'm not trying to hide anything. This is the complete code that I wrote so far.
/*
SD card basic file example
This example shows how to create and destroy an SD card file
The circuit:
* SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 4
created Nov 2010
by David A. Mellis
modified 9 Apr 2012
by Tom Igoe
This example code is in the public domain.
*/
#include <SPI.h>
#include <SD.h>
const int chipSelect = 10; File root; int x, L, s; char z[30]; String G, H, J;
void setup() {
Serial.begin(9600); pinMode(10, OUTPUT);
Serial.print("Initializing SD card...");
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present"); return; }
Serial.println("card initialized."); delay(20); }
void loop() {x= Serial.peek();
switch (x) {
case -1: L=-1; G=""; J=""; break;
default: L = L + 1; delay(10); z[L]= Serial.read(); G += String(z[L]); }
G.toUpperCase(); G.trim(); J=G.substring(0, 5);
if (G == "DIR") {L = L + 1; z[L]= Serial.read(); Serial.println("DIR:");
root = SD.open("/"); printDirectory(root, 0); }
else if (J == "READ") {
J=G.substring(5); readFile();
}
else if (J == "EDIT") {
editFile();
} }
void printDirectory(File dir, int numTabs) {
while(true) {File entry = dir.openNextFile();
if (! entry) {dir.rewindDirectory(); break; }
for (uint8_t i=0; i<numTabs; i++) {Serial.print('\t'); }
Serial.print(entry.name());
if (entry.isDirectory()) {Serial.println("/");
printDirectory(entry, numTabs+1); }
else {Serial.print("\t\t"); Serial.println(entry.size(), DEC); }
entry.close(); Serial.print('\n'); }}
void readFile() {
File dataFile = SD.open(J);
if (dataFile) {
while (dataFile.available()) {
Serial.write(dataFile.read());
}
dataFile.close(); Serial.println();
}
else {
Serial.print("error opening "); Serial.println(J);
}}
void editFile() {
}
I didn't want to post the entire sketch because I thought it
might be too long and nobody would bother replying to my topic.
Can you tell me why my sketch is not accepting the string variable?
The open function in the SD library is defined like this:
SDClass::open(const char *filepath, uint8_t mode)
I can't find any overloading of the function. This means that the only thing it will accept for the filename is a C-style null-terminated string which is what you give it when you use SD.open("XYTABLE.TXT"). But a String will not work. You will have to convert the String to a string (I think there is a method to do that but I don't use Strings).
Pete
For starters, it doesn't compile, I'm not quite sure what that is about
for seconds you are using chipselect = 10. The example comment clearly says CS - pin 4. Why do you want to change it?
I'm afraid the rest of it is beyond me, but just as likely too convoluted to merit reading, particularly as the objective is unclear.
Below is a stripped-down version of what I am using, and might provide some direction. Files are named by date. They are retrieved by just sending MMDD. The vital bit is the readstring section.
//Daily files can be read via bluetooth by sending MMDD
#include "Wire.h"
#include <SD.h>
#include <SPI.h> // SD
#include <string.h> //Date As Filename
#define DS1307_ADDRESS 0x68
char filename[13],charBuf [13],strYr[4];
File myFile, dumpFile;
int second, minute, hour, weekDay, monthDay, month, year;
const int chipSelect = 4;
String readString;
String stringFive, stringSix;
void setup() {
Wire.begin();
Serial.begin(115200);
Serial.println("Bare test");
Serial.println("Init SD CARD");
// make sure that the default chip select pin 53 is set to
// output, even if you don't use it:
pinMode(10, OUTPUT);//Uno 10, MEGA 53
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect))
{
Serial.println("Card failed");
// don't do anything more:011
return;
}
Serial.println("CARD OK");
delay(2000);
GetClock();
Serial.println("today's date for filename");
Serial.print(year);
Serial.print("/");
Serial.print(month);
Serial.print("/");
Serial.println(monthDay);
getFileName();
Serial.println("active filename");
Serial.println(filename);
delay(2000);
}
void loop() {
GetClock();
while (Serial.available())
{
delay(3);
char c = Serial.read();
readString += c;
}// end while
if (readString.length() >0)
{
getDump();
readString="";
} // end if
}
byte bcdToDec(byte val) {
// Convert binary coded decimal to normal decimal bers
return ( (val/16*10) + (val%16) );
}
void getFileName(){
sprintf(filename, "%04u%02u%02u.csv", year, month, monthDay);
}
void GetClock(){
// Reset the register pointer
Wire.beginTransmission(DS1307_ADDRESS);
byte zero = 0x00;
Wire.write(zero);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDRESS, 7);
second = bcdToDec(Wire.read());
minute = bcdToDec(Wire.read());
hour = bcdToDec(Wire.read() & 0b111111); //24 hour time
weekDay = bcdToDec(Wire.read()); //0-6 -> sunday - Saturday
monthDay = bcdToDec(Wire.read());
month = bcdToDec(Wire.read());
year = bcdToDec(Wire.read());
year = year +2000;
}
void getDump() {
dtostrf(year,4, 0, strYr);
stringSix = strYr + readString + ".csv";
stringSix.toCharArray(charBuf, 15);
File dumpFile = SD.open(charBuf);
if (dumpFile)
{
Serial.println("DUMP FROM ");
Serial.println(charBuf);
delay(2000);
while (dumpFile.available())
{
Serial.write(dumpFile.read());
}
dumpFile.close();
}
else {
Serial.println("error opening file");
}
}
.
KMOver50:
I didn't want to post the entire sketch because I thought it
might be too long and nobody would bother replying to my topic.
Can you tell me why my sketch is not accepting the string variable?
Since the line failed on opening "J" it would have helped to see the data type of J, eh?
We are more likely to move on from a snippet than an entire piece of code where we can see at a glance what the problem is.
Try:
File dataFile = SD.open(J.c_str ());
Now:
int x, L, s; char z[30]; String G, H, J;
You certainly have a way with variable names. x, L, s, z, G, H, J.
Are long variable names expensive?
How will you debug this stuff?
default: L = L + 1; delay(10); z[L]= Serial.read(); G += String(z[L]); }
z[L]= Serial.read(); G += String(z[L]);
Since Serial.read() returns one character, why do you feel it necessary to create and destroy a String to wrap ONE character, when the += operator is perfectly capable of appending a character to a String?
Thank you both Nick G and Nick_p. I finally got it to work. Sorry about the single letter variable names. I do this because I'm not a fan of typing. And on the contrary, it is very easy to debug I know what each variable is doing. Nick_p I used the code you posted and integrated it into my sketch and it worked fine. I have no idea why it works but it works. The project I'm working on is an XY table that will be controlled by my Arduino Uno. The sketch will show me the files on an SD card. Then I type in the name of the file I want to open. It then opens the file and moves the XY table according to the program on the SD card. I thank all of you for your replies.
I know what each variable is doing.
You do today, yes. Come back in a year and tell me that. Some IDEs (not the default one) will auto-complete variable names for you. Personally I copy and paste if they are a bit long.
Hi I am having the same problem but I think my issue could be to do with me not really understanding the SD card system and setup compared to creating the file reference on the fly..
my code (crude test code) is...
/*
SD card datalogger
This example shows how to log data from three analog sensors
to an SD card using the SD library.
The circuit:
* SD card attached to SPI bus as follows:
** UNO: MOSI - pin 11, MISO - pin 12, CLK - pin 13, CS - pin 4 (CS pin can be changed)
and pin #10 (SS) must be an output
** Mega: MOSI - pin 51, MISO - pin 50, CLK - pin 52, CS - pin 4 (CS pin can be changed)
and pin #52 (SS) must be an output
** Leonardo: Connect to hardware SPI via the ICSP header
Pin 4 used here for consistency with other Arduino examples
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>
#include <Wire.h>
#include "RTClib.h"
RTC_DS1307 RTC;
// 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 = 10;
int LogFrequency = 5000;
String DataFileName;
unsigned long LastMillis;
File dataFile;
void setup()
{ Wire.begin();
RTC.begin();
LastMillis = millis();
// Open serial communications and wait for port to open:
Serial.begin(57600);
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(SS, 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:
while (1) ;
}
Serial.println("card initialized.");
}
float GetAveragePinReads(){
byte Ctr=0;
float SumOfReads=0.0;
// get the prescribed number of samples
while (Ctr < 100){
SumOfReads = SumOfReads + analogRead(0);
Ctr++;
}
//Calculate and return the average
return SumOfReads/Ctr;
}
void loop()
{
if (millis() >= LastMillis + LogFrequency) {;
// make a string for assembling the data to log:
String dataString = "";
DateTime now = RTC.now();
// read three sensors and append to the string:
int sensor = analogRead(0);
float MyVolts = 5*GetAveragePinReads()*1.0/1024*11.3832;
if (now.hour()<10) { dataString += ('0');}
dataString += String(now.hour(), DEC);
dataString += (':');
if (now.minute()<10) { dataString += ('0');}
dataString += String(now.minute(), DEC);
dataString += (':');
if (now.second()<10) { dataString += ('0');}
dataString += String(now.second(), DEC);
dataString += " ";
dataString += String(MyVolts);
dataString += " ";
// work out the name of the datafile
DataFileName = (""); // need to reste it all the time as we want it date based
DataFileName += String(now.year(), DEC);
if (now.month()< 10) {DataFileName += ('0');}
DataFileName += String(now.month(), DEC);
if (now.day()< 10) {DataFileName += ('0');}
DataFileName += String(now.day(), DEC);
DataFileName += ("_LOG.CSV");
// Open up the file we're going to log to!
dataFile = SD.open(DataFileName.c_str(), FILE_WRITE);
if (! dataFile) {
Serial.println("error opening file");
// Wait forever since we cant write data
while (1) ;
}
dataFile.println(dataString);
// print to the serial port too:
Serial.println(dataString);
// The following line will 'save' the file to the SD card after every
// line of data - this will use more power and slow down how much data
// you can read but it's safer!
// If you want to speed up the system, remove the call to flush() and it
// will save the file only every 512 bytes - every time a sector on the
// SD card is filled with data.
dataFile.flush();
dataFile.close();
}
}
I want to test for the date each time i am going to log as I want to always use the current date for the file and obviously this will flip over in the post time at midnight.
Anyway the error i get is that it simply will not open the file:
My serial output is thus....
Initializing SD card...card initialized.
error opening file
Hope someone can help me.
Kindest regards.
Graham.
DataFileName = (""); // need to reste it all the time as we want it date based
(The) (parentheses) (are) (useless).
DataFileName += String(now.year(), DEC);
The base is 10 unless you specify otherwise. Look like you know that and loose the , DEC part.
What, exactly, is the file name going to be? It looks to me like today's file name would be either 20151111_LOG.CSV or 151111_LOG.CSV, depending on whether year() returns 15 or 2015.
In either case, count the number of characters in the name. Does that name LOOK like it is in the required 8.3 format? Not to me it doesn't. So, the fact that it can't create the file comes as no surprise to me.
GrahamPrattWyalong:
// 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 = 10;// make sure that the default chip select pin is set to
// output, even if you don't use it:
pinMode(SS, OUTPUT);Initializing SD card...card initialized.
error opening file
I'm amazed the card initialised.
The line
pinMode(SS, OUTPUT);
is meaningless, and you would be better off calling pin 10 like everybody else does, thereby using the code like Igoe intended.
While your comments refer to using an SD card on an Ethernet shield, you don't say if you are doing that. Even if you aren't, it might help to use the same logic and use pin 4 for CS.
Der..... 8.3. Ok no probs... I think I will drop the "_LOG" that should fix it.
All I have done is take the example provided and changed the logging to log a voltage read at pin 0 and a modification to the file name construct.
I was not aware that the example provided by the community contained extraneous characters.
I shall make it do the file name thing then I shall get rid of the extras.
Thankyou for your help PaulS.
And Nick_Pyner, as above this is a copy and paste of an example else why would I put someone else's name as credit for writing it. And no it has soldered links to pin 10, I could go to the trouble of changing it to pin 4 but that would appear to be a waste of effort.
Nonetheless thank your for your assistance.
Kindest regards,
Graham.
And Nick_Pyner, as above this is a copy and paste of an example else why would I put someone else's name as credit for writing it. And no it has soldered links to pin 10, I could go to the trouble of changing it to pin 4 but that would appear to be a waste of effort.
It's not that you need to change the pin. The issue is that SS doesn't appear anywhere else in the code you posted, so it is not obvious what the value of SS is. We do not like stuff happening magically in code we look at. We like to know what it happening. Some stuff, like that fact that SD uses SPI we know and accept. Magic stuff, like some library assigning a value to SS, we don't.
SS is supposed to be a standard define for the Slave Select line, for SPI, for the currently-selected board.
It's in pins_arduino.h:
static const uint8_t SS = 10;
static const uint8_t MOSI = 11;
static const uint8_t MISO = 12;
static const uint8_t SCK = 13;
I think that is more valid to use SS than hard-coding in a 10 into your code.
filename.c_str()
I post this not to bring the thread back alive, but to confirm for others, in my 2hr search, what I almost missed - a one line solution; I, like most everyone else, read the pertinent question and immediately scroll to the bottom to save time.
Thank you Nick!
Below is simplistic example to Nick's solution; I'm not concerned about memory or leakage due to usage of stings.
Arduino Rev 1.8.1
File SDcmd;
String SDfile = "TEST.txt";
void setup(void) {
//initialize my TFT touchscreen
//initialize my SD card
ReadFile(SDfile);
}
void ReadFile(String filename){
if (SD.exists(filename.c_str())) {tft.println(filename + " exists.");delay(2000);}
else {tft.println(filename + " doesn't exist.");delay(5000);return;}
SDcmd = SD.open(filename.c_str());
if (SDcmd) {
//Read SD file here
SDcmd.close();
}
I'm not concerned about memory or leakage due to usage of stings.
C strings do not leak memory. Strings can, and you SHOULD be concerned if you use them.
Thank you, Paul. When I shed my hobbyist paradigm and obtain the pinnacle of your engineering intelligence, I will.
Regards.