I have some quite long code to copy a file from an SD to the same card, but under a different name. I have one version where the file names are built-in to the sketch, but to make it more portable, i have implemented a serial file selection system. There are 3 phases; choosing the input file, choosing the output file, and the copying. I have included checks to see if the files exist, confirmation for the names and so on. However, in the middle of my code, just before the copying phase, the input file disappears, and the arduino does not find it. It finds it in the first phase, finds the output file, but can't find the input file in the copying phase. SD.exists() and SD.open() both confirm this, but if i remove the sd card and put it into a computer, the file is there.
My code:
#include <SoftReset.h>
#include <SPI.h>
#include <SdFat.h>
#define csPin 4
#define roundTo 64
SdFat SD;
File sourceFile;
File outputFile;
int bufSize;
uint32_t lastPos = 0, timeLast, timeNow;
#define BAUD 115200
//make timeBetweenChars the time it takes (in milliseconds) between each char being transferred over serial, and make it at least one
unsigned long timeBetweenChars = max(1000 / (BAUD / 8), 1);
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(BAUD);
while (!Serial) {
}
Serial.print(F("Initializing SD card..."));
/******************************************************************************/
// see if the card is present and can be initialized:
if (!SD.begin(csPin, SPI_FULL_SPEED)) {
Serial.println(F("Card failed, or not present"));
soft_restart();
}
Serial.println("Card initialized.");
Serial.println(F("Files on card:"));
SD.ls();
//******************************************************************************
//START OF INPUT FILE NAME
//******************************************************************************
enterInputFileName:
Serial.println(F("Enter input file name:"));
while (!Serial.available()); // wait till serial available
char inbuf[64];
/******************************************************************************/
//create variable to store how many valid (not newline or carriage return) chars have been read
byte pos = 0;
//read into array as long as not getting newline char
while (Serial.available() && Serial.peek() != '\n' && Serial.peek() != '\r') {
inbuf[pos] = Serial.read();
pos++;
//wait so there is time for the next char to arrive in the buffer
delay(timeBetweenChars);
}
while (Serial.available() && (Serial.peek() == '\n' || Serial.peek() == '\r')) {
//remove next char from buffer
Serial.read();
//wait in case there is another char to be cleared
delay(timeBetweenChars);
}
//end array at pos
inbuf[pos] = '0円';
/******************************************************************************/
//create new array with correct size for input file name
char inputFileName[pos];
//copy data from inbuf to inputFileName
strcpy(inputFileName, inbuf);
/******************************************************************************/
//confirm if you entered the right file name
confirmInputFileName:
Serial.print(F("Select file '"));
Serial.print(inputFileName);
Serial.println(F("'? Y/N"));
while (!Serial.available()); // wait till serial available
char confirm = Serial.read();
bool tooManyChars = 0;
// wait for the next chars
delay(timeBetweenChars);
while (Serial.available()) {
//read next char
char extra = Serial.read();
//if it isn't a newline or carriage return
if (extra != '\n' && extra != '\r') {
//too many characters entered
tooManyChars = 1;
}
//wait for next char
delay(timeBetweenChars);
}
/******************************************************************************/
if (tooManyChars) {
Serial.println(F("Enter 1 char"));
goto confirmInputFileName;
}
//if only 1 char entered
switch (confirm) {
case 'Y': break;
case 'y': break;
case 'N': goto enterInputFileName; break;
case 'n': goto enterInputFileName; break;
//invalid char entered
default: Serial.println(F("Enter Y/N")); goto confirmInputFileName; break;
}
/******************************************************************************/
Serial.print(F("Using file '"));
Serial.print(inputFileName);
Serial.println("' as input file");
if (SD.exists(inputFileName)) {
Serial.println(F("File exists"));
}
else {
Serial.println(F("Error, file does not exist. Please choose another file"));
goto enterInputFileName;
}
//******************************************************************************
//END OF INPUT FILE NAME
//******************************************************************************
//******************************************************************************
//START OF OUTPUT FILE NAME
//******************************************************************************
enterOutputFileName:
//clear input buffer
for (byte w = 0; w < (sizeof(inbuf) / sizeof(inbuf[0])); w++) {
inbuf[w] = '0円';
}
Serial.println(F("Enter output file name:"));
//wait for some chars to arrive
while (!Serial.available());
/******************************************************************************/
//create variable to store how many valid (not newline or carriage return) chars have been read
pos = 0;
//found here Serial.print(SD.exists(inputFileName));
//read into array as long as not getting newline char
while (Serial.available() && Serial.peek() != '\n' && Serial.peek() != '\r') {
//file found on first loop, but not after that
// found here Serial.println(SD.exists(inputFileName));
inbuf[pos] = Serial.read();
//not found here Serial.println(SD.exists(inputFileName));
pos++;
//wait so there is time for the next char to arrive in the buffer
delay(timeBetweenChars);
}
//input file not found here
while (Serial.available() && (Serial.peek() == '\n' || Serial.peek() == '\r')) {
//remove next char from buffer
Serial.read();
//wait in case there is another char to be cleared
delay(timeBetweenChars);
}
//end array at pos
inbuf[pos] = '0円';
/******************************************************************************/
//create new array with correct size for output file name
char outputFileName[pos];
//copy data from inbuf to outputFileName
strcpy(outputFileName, inbuf);
/******************************************************************************/
//confirm if you entered the right file name
confirmOutputFileName:
Serial.print(F("Select file '"));
Serial.print(outputFileName);
Serial.println(F("'? Y/N"));
while (!Serial.available()); // wait till serial available
confirm = Serial.read();
tooManyChars = 0;
// wait for the next chars
delay(timeBetweenChars);
while (Serial.available()) {
//read next char
char extra = Serial.read();
//if it isn't a newline or carriage return
if (extra != '\n' && extra != '\r') {
//too many characters entered
tooManyChars = 1;
}
//wait for next char
delay(timeBetweenChars);
}
/******************************************************************************/
if (tooManyChars) {
Serial.println(F("Enter 1 char"));
goto confirmOutputFileName;
}
//if only 1 char entered
switch (confirm) {
case 'Y': break;
case 'y': break;
case 'N': goto enterOutputFileName; break;
case 'n': goto enterOutputFileName; break;
//invalid char entered
default: Serial.println(F("Enter Y/N")); goto confirmOutputFileName; break;
}
/******************************************************************************/
Serial.print(F("Using file '"));
Serial.print(outputFileName);
Serial.println("' as output file");
if (SD.exists(outputFileName)) {
Serial.println(F("File already exists. Overwrite? Y/N"));
confirmOverwrite:
while (!Serial.available()); // wait till serial available
confirm = Serial.read();
tooManyChars = 0;
// wait for the next chars
delay(timeBetweenChars);
while (Serial.available()) {
//read next char
char extra = Serial.read();
//if it isn't a newline or carriage return
if (extra != '\n' && extra != '\r') {
//too many characters entered
tooManyChars = 1;
}
//wait for next char
delay(timeBetweenChars);
}
/******************************************************************************/
if (tooManyChars) {
Serial.println(F("Enter 1 char"));
goto confirmOverwrite;
}
//if only 1 char entered
bool overwrite = 0;
switch (confirm) {
case 'Y': overwrite = 1; break;
case 'y': overwrite = 1; break;
case 'N': goto enterOutputFileName; break;
case 'n': goto enterOutputFileName; break;
//invalid char entered
default: Serial.println(F("Enter Y/N")); goto confirmOverwrite; break;
}
if (overwrite) {
Serial.println(F("Removing output file"));
SD.remove(outputFileName);
}
}
Serial.println(F("Creating output file"));
outputFile = SD.open(outputFileName, FILE_WRITE);
outputFile.close();
//******************************************************************************
//END OF OUTPUT FILE NAME
//******************************************************************************
//******************************************************************************
//START OF COPYING
//******************************************************************************
//read source and write to output
sourceFile = SD.open(inputFileName);
timeLast = micros();
//debug line
Serial.print(F("Exists(I,O): ")); Serial.print(SD.exists(inputFileName)); Serial.println(SD.exists(outputFileName));
if (sourceFile) {
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
Serial.println(F("Copying"));
while (sourceFile.available()) {
//make buffer size 50% of free ram, or amount of bytes left if smaller
int ram = round((freeRam() * 0.5) / roundTo) * roundTo;
bufSize = min(ram, sourceFile.available());
//round to nearest value of roundTo
bufSize = round(bufSize / roundTo) * roundTo;
byte data[bufSize];
sourceFile.readBytes(data, bufSize);
lastPos = sourceFile.position();
sourceFile.close();
outputFile = SD.open(outputFileName, O_APPEND | O_WRITE);
outputFile.write(data, bufSize);
outputFile.close();
sourceFile = SD.open(inputFileName);
sourceFile.seek(lastPos);
//Serial.print(F("\tExists(I,O): ")); Serial.print(SD.exists(inputFileName)); Serial.print(SD.exists(outputFileName));
printStats();
}
sourceFile.close();
outputFile.close();
Serial.println(F("Finished"));
unsigned int sec = millis() / 1000;
unsigned int mins = sec / 60;
sec -= mins * 60;
Serial.print(F("Time taken: "));
Serial.print(mins);
Serial.print(':');
Serial.print(sec);
Serial.print(F("Speed: "));
Serial.print((sourceFile.size() / 1000) / (millis() / 1000));
Serial.print(F(" KB/s"));
}
// if the file isn't open, pop up an error:
else {
Serial.println("error opening files");
Serial.print(F("Exists(I,O): ")); Serial.print(SD.exists(inputFileName)); Serial.println(SD.exists(outputFileName));
SD.initErrorHalt();
}
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
delay(200);
digitalWrite(LED_BUILTIN, LOW);
delay(200);
}
int freeRam () {
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
void printStats() {
timeNow = micros();
uint32_t timeTaken = timeNow - timeLast;
float sent = sourceFile.position() / 1000.0;
float fileSize = sourceFile.size() / 1000.0;
float left = fileSize - sent;
float speed;
speed = 1000000UL / timeTaken * bufSize;
speed /= 1000;
unsigned int sec = millis() / 1000;
unsigned int mins = sec / 60;
sec -= mins * 60;
int estSec = (left / speed);
int estMin = estSec / 60;
estSec -= estMin * 60;
Serial.print(F("\tSent (KB): ")); Serial.print(sent);
Serial.print(F("\tLeft (KB): ")); Serial.print(left);
Serial.print(F("\tSize (KB): ")); Serial.print(fileSize);
Serial.print(F("\tTime: ")); Serial.print(mins); Serial.print(':'); Serial.print(sec);
Serial.print(F("\tSpeed (KB/sec): ")); Serial.print(speed);
Serial.print(F("\tFree: ")); Serial.print(freeRam() - bufSize);
Serial.print(F("\tBuf: ")); Serial.print(bufSize);
Serial.print(F("\tEST: ")); Serial.print(estMin); Serial.print(':'); Serial.print(estSec);
//Serial.print(F("\tExists(I,O): ")); Serial.print(SD.exists(inputFileName)); Serial.print(SD.exists(outputFileName));
Serial.print('\n');
timeLast = micros();
}
My serial output(I chose Nova.mp3 as input file and output.mp3 as output file):
Initializing SD card...Card initialized.
Files on card:
output.mp3
Nova.mp3
Eclipse.wav
Enter input file name:
Select file 'Nova.mp3'? Y/N
Using file 'Nova.mp3' as input file
File exists
Enter output file name:
Select file 'output.mp3'? Y/N
Using file 'output.mp3' as output file
File already exists. Overwrite? Y/N
Removing output file
Creating output file
Exists(I,O): 01
error opening files
Exists(I,O): 01
No error found.
I know i am not running out of memory, and have no compiler errors, but i have no clue what is going wrong. Is it possible I have put in some unnecessary code? Also, if it was possible to have multiple files open at a time, would i spped up my read/write speed?
-
you char array for inputFileName is one char shorter then needed. if 0 is at pos you need to allocate and copy pos + 1 chars because the indexing starts at 0. (btw: your code is very ineffective)Juraj– Juraj ♦07/19/2018 10:56:52Commented Jul 19, 2018 at 10:56
-
I know indexing starts at 0, and if you didn't notice, i actually add 1 to pos at the end of the loop, so the array should be long enough. And how is it ineffective? do you mean inefficient? Edit: this actually made my code start copying, will update if it finishes successfully.user35284– user3528407/20/2018 07:32:38Commented Jul 20, 2018 at 7:32
-
too much code for the taskJuraj– Juraj ♦07/20/2018 09:57:11Commented Jul 20, 2018 at 9:57
-
How should i compress it? i can't do the copy loop as simply as i'd like, because only 1 file can be opened at a time, and i put the file selection confirms in because sometimes people make typos. what can i make smalleruser35284– user3528407/20/2018 23:29:21Commented Jul 20, 2018 at 23:29
-
"As of version 1.0, the library supports opening multiple files." arduino.cc/en/Reference/SDJuraj– Juraj ♦07/21/2018 04:47:24Commented Jul 21, 2018 at 4:47
1 Answer 1
The problem is that you have the char arrays for zero terminated c-string one char shorter then needed. I see you assign the terminating zero to inbuf[pos] and then you allocate pos chars for the filename and copy pos count of characters. That leaves out the terminating zero and the c-string of the filename is not terminated by 0 and continues in memory with random content.