Файл не найден на SD-карте

У меня есть довольно длинный код для копирования файла с SD на ту же карту, но под другим именем. У меня есть одна версия, в которой имена файлов встроены в скетч, но чтобы сделать его более переносимым, я реализовал последовательную систему выбора файлов. Есть 3 фазы: выбор входного файла, выбор выходного файла и копирование. Я включил проверки, чтобы увидеть, существуют ли файлы, подтверждение имен и так далее. Однако в середине моего кода, как раз перед фазой копирования, входной файл исчезает, и arduino не находит его. Он находит его на первой фазе, находит выходной файл, но не может найти входной файл на фазе копирования. SD.exists() и SD.open() оба подтверждают это, но если я извлеку SD-карту и вставлю ее в компьютер, файл будет там.

Мой код:

#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
//сделать timeBetweenChars временем (в миллисекундах), которое проходит между передачей каждого символа по последовательному порту, и сделать его не менее одного
unsigned long timeBetweenChars = max(1000 / (BAUD / 8), 1);

void setup() {
  // Открываем последовательную связь и ждем открытия порта:
  Serial.begin(BAUD);
  while (!Serial) {
  }
  Serial.print(F("Initializing SD card..."));
  /******************************************************************************/
  // проверяем, присутствует ли карта и может ли она быть инициализирована:
  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();
  //***************************************************************************
  //НАЧАЛО ИМЕНИ ВХОДНОГО ФАЙЛА
  //***************************************************************************
enterInputFileName:
  Serial.println(F("Enter input file name:"));
  while (!Serial.available()); // ждем, пока серийный номер станет доступен
  char inbuf[64];
  /******************************************************************************/
  //создаем переменную для хранения количества прочитанных допустимых символов (не символов новой строки или возврата каретки)
  byte pos = 0;
  //читаем в массив, пока не получим символ новой строки
  while (Serial.available() && Serial.peek() != '\n' && Serial.peek() != '\r') {
    inbuf[pos] = Serial.read();
    pos++;
    //ждем, пока следующий символ не поступит в буфер
    delay(timeBetweenChars);
  }
  while (Serial.available() && (Serial.peek() == '\n' || Serial.peek() == '\r')) {
    //удаляем следующий символ из буфера
    Serial.read();
    //ждем, если есть еще один символ для очистки
    delay(timeBetweenChars);
  }
  //конец массива в позиции pos
  inbuf[pos] = '\0';
  /******************************************************************************/
  //создаем новый массив с правильным размером для имени входного файла
  char inputFileName[pos];
  //копируем данные из inbuf в inputFileName
  strcpy(inputFileName, inbuf);
  /******************************************************************************/
  //подтвердите, что вы ввели правильное имя файла
confirmInputFileName:
  Serial.print(F("Select file '"));
  Serial.print(inputFileName);
  Serial.println(F("'? Y/N"));

  while (!Serial.available()); // ждем, пока серийный номер станет доступен
  char confirm = Serial.read();
  bool tooManyChars = 0;
  // ждем следующих символов
  delay(timeBetweenChars);
  while (Serial.available()) {
    //читаем следующий символ
    char extra = Serial.read();
    //если это не символ новой строки или возврата каретки
    if (extra != '\n' && extra != '\r') {
      //введено слишком много символов
      tooManyChars = 1;
    }
    //ждем следующего символа
    delay(timeBetweenChars);
  }
  /******************************************************************************/
  if (tooManyChars) {
    Serial.println(F("Enter 1 char"));
    goto confirmInputFileName;
  }

  //если введен только 1 символ
  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;
  }
  /******************************************************************************/
  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;
  }
  //***************************************************************************
  //КОНЕЦ ИМЕНИ ВХОДНОГО ФАЙЛА
  //************************************************ ******************************


  //************************************************ ******************************
  //НАЧАЛО ИМЕНИ ВЫХОДНОГО ФАЙЛА
  //************************************************ ******************************
enterOutputFileName:
  //очистить входной буфер
  for (byte w = 0; w < (sizeof(inbuf) / sizeof(inbuf[0])); w++) {
    inbuf[w] = '\0';
  }
  Serial.println(F("Enter output file name:"));
  //ждем, пока поступят некоторые символы
  while (!Serial.available());
  /******************************************************************************/
  //создаем переменную для хранения количества прочитанных допустимых символов (не символов новой строки или возврата каретки)
  pos = 0;
  //найдено здесь Serial.print(SD.exists(inputFileName));
  //читаем в массив, пока не получим символ новой строки
  while (Serial.available() && Serial.peek() != '\n' && Serial.peek() != '\r') {
    //файл найден в первом цикле, но не после него
    // найдено здесь Serial.println(SD.exists(inputFileName));
    inbuf[pos] = Serial.read();
    //здесь не найдено Serial.println(SD.exists(inputFileName));
    pos++;
    //ждем, пока следующий символ не поступит в буфер
    delay(timeBetweenChars);
  }
  //входной файл здесь не найден
  while (Serial.available() && (Serial.peek() == '\n' || Serial.peek() == '\r')) {
    //удаляем следующий символ из буфера
    Serial.read();
    //ждем, если есть еще один символ для очистки
    delay(timeBetweenChars);
  }
  //конец массива в позиции pos
  inbuf[pos] = '\0';
  /******************************************************************************/
  //создаем новый массив с правильным размером для имени выходного файла
  char outputFileName[pos];
  //копируем данные из inbuf в outputFileName
  strcpy(outputFileName, inbuf);
  /******************************************************************************/
  //подтвердите, что вы ввели правильное имя файла
confirmOutputFileName:
  Serial.print(F("Select file '"));
  Serial.print(outputFileName);
  Serial.println(F("'? Y/N"));

  while (!Serial.available()); // ждем, пока серийный номер станет доступен
  confirm = Serial.read();
  tooManyChars = 0;
  // ждем следующих символов
  delay(timeBetweenChars);
  while (Serial.available()) {
    //читаем следующий символ
    char extra = Serial.read();
    //если это не символ новой строки или возврата каретки
    if (extra != '\n' && extra != '\r') {
      //введено слишком много символов
      tooManyChars = 1;
    }
    //ждем следующего символа
    delay(timeBetweenChars);
  }
  /******************************************************************************/
  if (tooManyChars) {
    Serial.println(F("Enter 1 char"));
    goto confirmOutputFileName;
  }

  //если введен только 1 символ
  switch (confirm) {
    case 'Y': break;
    case 'y': break;
    case 'N': goto enterOutputFileName; break;
    case 'n': goto enterOutputFileName; break;
    //введен неверный символ
    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()); // ждем, пока серийный номер станет доступен
    confirm = Serial.read();
    tooManyChars = 0;
    // ждем следующих символов
    delay(timeBetweenChars);
    while (Serial.available()) {
      //читаем следующий символ
      char extra = Serial.read();
      //если это не символ новой строки или возврата каретки
      if (extra != '\n' && extra != '\r') {
        //введено слишком много символов
        tooManyChars = 1;
      }
      //ждем следующего символа
      delay(timeBetweenChars);
    }
    /******************************************************************************/
    if (tooManyChars) {
      Serial.println(F("Enter 1 char"));
      goto confirmOverwrite;
    }

    //если введен только 1 символ
    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;
      //введен неверный символ
      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();
  //***************************************************************************
  //КОНЕЦ ИМЕНИ ВЫХОДНОГО ФАЙЛА
  //***************************************************************************


  //***************************************************************************
  //НАЧАЛО КОПИРОВАНИЯ
  //***************************************************************************
  //прочитать исходный код и записать в выходной файл
  sourceFile = SD.open(inputFileName);
  timeLast = micros();
  //строка отладки
  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()) {
      //сделать размер буфера равным 50% от свободной оперативной памяти или количеству оставшихся байтов, если меньше
      int ram = round((freeRam() * 0.5) / roundTo) * roundTo;
      bufSize = min(ram, sourceFile.available());
      //округлить до ближайшего значения 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"));
  }
  // если файл не открыт, вывести ошибку:
  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();
}

Мой последовательный вывод (я выбрал Nova.mp3 в качестве входного файла и output.mp3 в качестве выходного файла):

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.

Я знаю, что у меня не заканчивается память, и нет ошибок компилятора, но я понятия не имею, что не так. Возможно, я вставил какой-то ненужный код? Кроме того, если бы можно было открывать несколько файлов одновременно, ускорил бы я скорость чтения/записи?

, 👍1


1 ответ


Лучший ответ:

2

Проблема в том, что у вас есть массивы символов для c-строки, завершающейся нулем, на один символ короче, чем нужно. Я вижу, что вы назначаете завершающий ноль inbuf[pos], а затем выделяете pos символов для имени файла и копируете pos количество символов. Это исключает завершающий ноль, а c-строка имени файла не завершается 0 и продолжается в памяти со случайным содержимым.

,