Считать одно значение из непрерывного потока данных с более низкой частотой

Я работаю над выпускным проектом университета и столкнулся с проблемой программирования Arduino после 3-летнего перерыва. У меня есть датчик давления, который отправляет данные о давлении с частотой 10 Гц. Я успешно запрограммировал свой Arduino Pro Micro на считывание данных по мере их поступления. Я использую библиотеку SoftwareSerial для установки цифровых контактов 8 и 9 на RX и TX соответственно. Поскольку датчик давления непрерывно передает значения данных, я считываю каждый символ, сохраняю их в массиве и вывожу на последовательный монитор. Я использую начальный и конечный маркеры для распознавания каждого входящего символа и помещения их в массив. Необработанные выходные данные от датчика имеют вид:

*000114.2927
*000114.2926
*000114.2927

И это код, который я использую.

#include <SoftwareSerial.h>
#define paroRX 8
#define paroTX 9

SoftwareSerial paroSerial(paroRX, paroTX);

const byte numChars = 16;
char paroChars[numChars];     //массив для хранения полученных данных
char paroToSend[numChars];      //массив для передачи
boolean newParoData = false;

void setup(){
  Serial.begin(9600);     //с радиопередатчиком
  paroSerial.begin(9600);
}

void loop(){
  paroSerial.listen();
  readParo();      //прочитать из Paro и сохранить значения в paroChars
  if(newParoData == true){
    for(int i=0; i<16; i++){
      paroToSend[i] = paroChars[i];
    }
    Serial.print(paroToSend); Serial.print('\n');     //передача данных Paro с новой строкой для каждого значения
    newParoData = false;
  }
}

void readParo(){
  static boolean receiveInProgress = false;
  static byte index = 0;
  char startMarker = '*';
  char endMarker = '\n';
  char pd;
  while(paroSerial.available() && newParoData == false){
    pd = paroSerial.read();
    if(receiveInProgress == true){
      if(pd != endMarker){
        paroChars[index] = pd;
        index++;
        if(index >= numChars){
          index = numChars - 1;
        }
      }else{
        paroChars[index] = '\0';      //завершить строку
        receiveInProgress = false;
        index = 0;
        newParoData = true;
      }
    }else if(pd == startMarker){
      receiveInProgress = true;
    }
  }
}

И вот что я получаю на выходе, и это правильно. Мне вообще не нужна эта * впереди.

000114.2927
000114.2928
000114.2927
000114.2929

Но все это происходит на частоте 10 Гц, что означает, что я получаю постоянный поток показаний на последовательном мониторе. Я хочу замедлить это до 1 Гц, или всего одного показания в секунду. Очевидным решением, которое пришло мне в голову, было добавить задержку в цикл. Поэтому я попытался реализовать это с помощью delay(1000) и также с помощью функции millis(), но я получаю странные показания, например:

000114.2927
***000114
**2928
***.000114
000114.2928

Как мне считывать данные с частотой 1 Гц из потока данных с частотой 10 Гц? Я хочу просто сохранять одно значение данных из потока в секунду в виде 000114.2927, а затем игнорировать остальное, пока Arduino делает что-то еще.

Спасибо!

, 👍1


3 ответа


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

2

Вы думаете в обратном порядке. Вы не хотите читать медленнее (что и делает задержка), вы хотите отправлять только каждую 10-ю запись.

То есть, считайте строки по мере их поступления. Когда вы достигнете 10, вы печатаете одну и сбрасываете счет.

Другими словами:

static int count = 0;

if(newParoData == true){

  count ++;
  if (count == 10) {
      count = 0;

      // (Зачем копировать строку только для того, чтобы отправить копию...?)
      for(int i=0; i<16; i++){
        paroToSend[i] = paroChars[i];
      }
      Serial.print(paroToSend); Serial.print('\n');     //передача данных Paro с новой строкой для каждого значения
  }
  newParoData = false;
}
,

1

Я бы использовал функции времени класса Stream. Serial — это реализация Stream. Свойство timeout (setTimeout) устанавливает, как долго функции блокировки будут ждать следующий байт/символ. Значение timeuot по умолчанию составляет 1000 мс.

Временные функции потока: find, parseInt, parseFloat, readBytes, readBytesUntil, readString и readStringUntil. (Неблокирующие функции чтения: read и peek.)

,

0

Ответ Маженко точен: вам просто нужно посчитать предложения, которые вы читать и повторять одно из десяти предложений. И вам не нужно две копии предложения, которое вы читаете: одного достаточно. Но я бы добавил, что вы даже не нужна ни одна копия: вы можете просто повторить символы, которые вы читать, не сохраняя их, пока не нажмете '*':

void loop()
{
    static uint8_t sentence_count;
    if (paroSerial.available()) {
        char c = paroSerial.read();
        if (c == '*') {
            // Увеличиваем количество предложений по модулю 10.
            if (++sentence_count == 10)
                sentence_count = 0;
        } else if (sentence_count == 0) {
            // Это одно предложение, которое нам придется повторить.
            Serial.write(c);
        }
    }
}

Кстати, если ваша программа не считывает входные данные с компьютера, и если он ничего не посылает на преобразователь, то вы можете рассмотреть избавляемся от SoftwareSerial и используем только аппаратный последовательный порт:

  • приемная часть порта для считывания данных с преобразователя
  • часть-эмиттер для отправки данных на компьютер.

Да, совершенно нормально использовать приемник и передатчик для разные ссылки. Единственное предостережение в том, что вам придется использовать те же самые конфигурация порта с обеих сторон: если преобразователь говорит на 9600 бит/с, то весь порт должен будет работать на скорости 9600 бит/с.

,