user35284: Is there a way to make the Arduino wait until all the serial data has been received? Say I send a 32 byte long string over serial (the length is actually random), how would I make my Arduino wait till all 32 bytes have been received?
If I execute Serial.readBytes straight away, it only reads one char. At the moment I'm using delay, but I'm worried that it is wasting time on the Arduino, as I would like it to be processed straight away. What I want to happen is I plug in my Arduino, type in the name of an input and output file, and it will start copying the input file into the output file. I don't want to re-upload every time I want to change files, so I would like to select over serial which ones to use. However, sometimes there will be no \r or \n sent at the end, which cuts off some of the file name.
Here is my code:
#include <SoftReset.h>
#include <SPI.h>
#include <SdFat.h>
#define csPin 4

SdFat SD;
File sourceFile;
File outputFile;

int bufSize;
uint32_t lastPos = 0, timeLast, timeNow;
//fix this (error: deprecated conversion from string constant to 'char*')
//char *outputFileName = "output.wav";
#define outputFileName "output.wav"
byte avail;

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  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.");
enterInputFileName:
  Serial.println(F("Enter input file name:"));
  while (!Serial.available()); // wait till serial available
  delay(100);
  avail = Serial.available() - 2; // extra '\n' & '\r' that we dont want
  char inbuf[avail];
  Serial.readBytes(inbuf, avail);
  Serial.read(); // remove '\n' from buffer
  Serial.read(); //remove '\r' from buffer
  inbuf[avail] = '\0';

  char inputFileName[avail];
  strcpy(inputFileName, inbuf);
confirmInputFileName:
  Serial.print(F("Select file '"));
  Serial.print(inputFileName);
  Serial.println(F("'? Y/N"));

  while (!Serial.available()); // wait till serial available
  delay(100); //wait for all chars to be recieved
  avail = Serial.available() - 2;
  if (avail > 1) {
    Serial.println(F("Enter 1 character"));
    while (Serial.available()) { //clear serial buffer
      Serial.read();
    }
    goto confirmInputFileName;
  }
  char confirm = Serial.read();
  Serial.read();
  Serial.read();
  switch (confirm) {
    case 'Y': break;
    case 'y': break;
    case 'N': goto enterInputFileName; break;
    case 'n': goto enterInputFileName; break;
    default: Serial.println(F("Enter Y/N")); goto confirmInputFileName; break;
  }
  //up to here works
  Serial.println(SD.exists(inputFileName));

  SD.remove(outputFileName);
  outputFile = SD.open(outputFileName, FILE_WRITE);
  outputFile.close();
  //read source and write to output
  sourceFile = SD.open(inputFileName);
  timeLast = micros();
  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
      bufSize = min((freeRam() * 0.5 ), sourceFile.available());
      byte data[bufSize];
      sourceFile.readBytes(data, bufSize);
      lastPos = sourceFile.position();
      sourceFile.close();
      outputFile = SD.open(outputFileName, O_APPEND | O_WRITE);
      outputFile.write(data, bufSize);
      printStats();
      outputFile.close();
      sourceFile = SD.open(inputFileName);
      sourceFile.seek(lastPos);
    }
    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.print("error opening files");
    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);
  //todo fix this line
  //Serial.print(F("\tExists(I,O): ")); Serial.print(SD.exists(inputFileName)); Serial.print(SD.exists(outputFileName));
  Serial.print('\n');
  timeLast = micros();
}


user37622: after source code was add to the question:
you use the readBytes function wrong. if you set the size parameter to count of available bytes it will read the count of available bytes. 
allocate a buffer and set the second parameter as the buffer size. the readBytes will try to fill the buffer, waiting max one second for the next character, which is enough to read the complete line sent from Serial Monitor.
if (Serial.available()) {    
  char buff[50];
  int length = Serial.readBytes(buff, sizeof(buff) - 1);
  buff[length] = 0; // terminating zero of c-string
  Serial.println(buff);
}

note: readBytes will wait one second after the last character received. better is to use readBytesUntil with expected terminator. or you can set smaller timeout on Serial with Serial.setTimeout(200) 

35284: <p>Is there a way to make the Arduino wait until all the serial data has been received? Say I send a 32 byte long string over serial (the length is actually random), how would I make my Arduino wait till all 32 bytes have been received?</p>

<p>If I execute <code>Serial.readBytes</code> straight away, it only reads one char. At the moment I'm using delay, but I'm worried that it is wasting time on the Arduino, as I would like it to be processed straight away. What I want to happen is I plug in my Arduino, type in the name of an input and output file, and it will start copying the input file into the output file. I don't want to re-upload every time I want to change files, so I would like to select over serial which ones to use. However, sometimes there will be no <code>\r</code> or <code>\n</code> sent at the end, which cuts off some of the file name.</p>

<p>Here is my code:</p>

<pre><code>#include &lt;SoftReset.h&gt;
#include &lt;SPI.h&gt;
#include &lt;SdFat.h&gt;
#define csPin 4

SdFat SD;
File sourceFile;
File outputFile;

int bufSize;
uint32_t lastPos = 0, timeLast, timeNow;
//fix this (error: deprecated conversion from string constant to 'char*')
//char *outputFileName = "output.wav";
#define outputFileName "output.wav"
byte avail;

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  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.");
enterInputFileName:
  Serial.println(F("Enter input file name:"));
  while (!Serial.available()); // wait till serial available
  delay(100);
  avail = Serial.available() - 2; // extra '\n' &amp; '\r' that we dont want
  char inbuf[avail];
  Serial.readBytes(inbuf, avail);
  Serial.read(); // remove '\n' from buffer
  Serial.read(); //remove '\r' from buffer
  inbuf[avail] = '\0';

  char inputFileName[avail];
  strcpy(inputFileName, inbuf);
confirmInputFileName:
  Serial.print(F("Select file '"));
  Serial.print(inputFileName);
  Serial.println(F("'? Y/N"));

  while (!Serial.available()); // wait till serial available
  delay(100); //wait for all chars to be recieved
  avail = Serial.available() - 2;
  if (avail &gt; 1) {
    Serial.println(F("Enter 1 character"));
    while (Serial.available()) { //clear serial buffer
      Serial.read();
    }
    goto confirmInputFileName;
  }
  char confirm = Serial.read();
  Serial.read();
  Serial.read();
  switch (confirm) {
    case 'Y': break;
    case 'y': break;
    case 'N': goto enterInputFileName; break;
    case 'n': goto enterInputFileName; break;
    default: Serial.println(F("Enter Y/N")); goto confirmInputFileName; break;
  }
  //up to here works
  Serial.println(SD.exists(inputFileName));

  SD.remove(outputFileName);
  outputFile = SD.open(outputFileName, FILE_WRITE);
  outputFile.close();
  //read source and write to output
  sourceFile = SD.open(inputFileName);
  timeLast = micros();
  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
      bufSize = min((freeRam() * 0.5 ), sourceFile.available());
      byte data[bufSize];
      sourceFile.readBytes(data, bufSize);
      lastPos = sourceFile.position();
      sourceFile.close();
      outputFile = SD.open(outputFileName, O_APPEND | O_WRITE);
      outputFile.write(data, bufSize);
      printStats();
      outputFile.close();
      sourceFile = SD.open(inputFileName);
      sourceFile.seek(lastPos);
    }
    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.print("error opening files");
    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) &amp;v - (__brkval == 0 ? (int) &amp;__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);
  //todo fix this line
  //Serial.print(F("\tExists(I,O): ")); Serial.print(SD.exists(inputFileName)); Serial.print(SD.exists(outputFileName));
  Serial.print('\n');
  timeLast = micros();
}
</code></pre>
 37622: <p>after source code was add to the question:</p>

<p>you use the readBytes function wrong. if you set the size parameter to count of available bytes it will read the count of available bytes. </p>

<p>allocate a buffer and set the second parameter as the buffer size. the readBytes will try to fill the buffer, waiting max one second for the next character, which is enough to read the complete line sent from Serial Monitor.</p>

<pre><code>if (Serial.available()) {    
  char buff[50];
  int length = Serial.readBytes(buff, sizeof(buff) - 1);
  buff[length] = 0; // terminating zero of c-string
  Serial.println(buff);
}
</code></pre>

<p>note: readBytes will wait one second after the last character received. better is to use readBytesUntil with expected terminator. or you can set smaller timeout on Serial with Serial.setTimeout(200) </p>
