Дождаться получения ВСЕХ последовательных данных.

Есть ли способ заставить Arduino ждать, пока не будут получены все последовательные данные? Допустим, я отправляю строку длиной 32 байта по последовательному порту (длина на самом деле случайна), как мне заставить Arduino ждать, пока не будут получены все 32 байта?

Если я сразу выполню Serial.readBytes, он прочитает только один символ. В данный момент я использую delay, но я беспокоюсь, что это тратит время на Arduino, так как я хотел бы, чтобы он обрабатывался немедленно. Я хочу, чтобы я подключал свой Arduino, вводил имя входного и выходного файла, и он начал копировать входной файл в выходной. Я не хочу повторно загружать каждый раз, когда хочу изменить файлы, поэтому я хотел бы выбрать, какие из них использовать, а не serial. Однако иногда в конце не будет отправлено \r или \n, что обрезает часть имени файла.

Вот мой код:

#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;
//исправить это (ошибка: устаревшее преобразование из строковой константы в 'char*')
//char *outputFileName = "output.wav";
#define outputFileName "output.wav"
byte avail;

void setup() {
  // Открываем последовательную связь и ждем открытия порта:
  Serial.begin(115200);
  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.");
enterInputFileName:
  Serial.println(F("Enter input file name:"));
  while (!Serial.available()); // ждем, пока серийный номер станет доступен
  delay(100);
  avail = Serial.available() - 2; // лишние '\n' и '\r', которые нам не нужны
  char inbuf[avail];
  Serial.readBytes(inbuf, avail);
  Serial.read(); // удалить '\n' из буфера
  Serial.read(); //удалить '\r' из буфера
  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()); // ждем, пока серийный номер станет доступен
  delay(100); //ждем получения всех символов
  avail = Serial.available() - 2;
  if (avail > 1) {
    Serial.println(F("Enter 1 character"));
    while (Serial.available()) { //очистить последовательный буфер
      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;
  }
  //до сих пор работает
  Serial.println(SD.exists(inputFileName));

  SD.remove(outputFileName);
  outputFile = SD.open(outputFileName, FILE_WRITE);
  outputFile.close();
  //прочитать исходный код и записать в выходной файл
  sourceFile = SD.open(inputFileName);
  timeLast = micros();
  if (sourceFile) {
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, HIGH);
    Serial.println(F("Copying"));
    while (sourceFile.available()) {
      //сделать размер буфера равным 50% от свободной оперативной памяти или количеству оставшихся байтов, если меньше
      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"));
  }
  // если файл не открыт, вывести ошибку:
  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 исправить эту строку
  //Serial.print(F("\tExists(I,O): ")); Serial.print(SD.exists(inputFileName)); Serial.print(SD.exists(outputFileName));
  Serial.print('\n');
  timeLast = micros();
}

, 👍-1


3 ответа


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

0

Вам необходимо отформатировать сообщение так, чтобы получатель знал, где заканчивается сообщение и начинается новое.

Затем на принимающей стороне вы заполняете буфер до тех пор, пока не будет получено полное сообщение, а затем обрабатываете сообщение.

Более продвинутая тактика — обрабатывать байты по мере их получения. Это требует построения конечного автомата, но означает, что вам не нужно хранить копию всего сообщения.

,

-1

Недавно я беспокоился о том же, когда работал над проектом, в котором нужно было считывать последовательные данные (полученные через Bluetooth HC-05).

Мой цикл loop() выполняет большую часть работы для моего проекта (Никогда больше не вводите пароль (устройство с сенсорным экраном на базе Arduino @ CodeProject.com ), но чтение последовательных данных также является большой частью работы.

Я реализовал следующий код, и он работает буквально несколько недель без каких-либо проблем.

if (Serial1.available() > 0) {
    incomingByte = Serial1.read();
    while (incomingByte > 0){
      if (incomingByte == 0x01){
        Keyboard.press(KEY_LEFT_CTRL);
        Keyboard.press(KEY_LEFT_ALT);
        Keyboard.write(KEY_DELETE);
        Keyboard.release(KEY_LEFT_ALT);
        Keyboard.release(KEY_LEFT_CTRL);
      }
      else{          
         Keyboard.write(incomingByte);
      }
      incomingByte = Serial1.read();
    }
  }

Вы можете видеть, что он считывает байты, пока они доступны, что может привести к мысли о его блокировке, но он справляется с этим очень хорошо.

Довольно много работы

Он выполняет довольно большую работу, поскольку захватывает все байты, поступающие через Bluetooth (а затем печатает их на подключенном компьютере — это долгая история, подробнее читайте в статье).

Довольно несколько байт

Он также считывает не менее 64 байтов (длина пароля + специальные байты управляющих символов), но справляется с этим довольно хорошо.

,

1

после добавления исходного кода к вопросу:

Вы неправильно используете функцию readBytes. Если вы установите параметр размера равным количеству доступных байтов, она будет считывать количество доступных байтов.

выделите буфер и установите второй параметр как размер буфера. readBytes попытается заполнить буфер, ожидая максимум одну секунду для следующего символа, чего достаточно для чтения всей строки, отправленной из Serial Monitor.

if (Serial.available()) {    
  char buff[50];
  int length = Serial.readBytes(buff, sizeof(buff) - 1);
  buff[length] = 0; // завершающий ноль c-строки
  Serial.println(buff);
}

Примечание: readBytes будет ждать одну секунду после получения последнего символа. Лучше использовать readBytesUntil с ожидаемым терминатором. Или вы можете установить меньшее время ожидания для Serial с помощью Serial.setTimeout(200)

,