Очистка буфера

PockerFace
Offline
Зарегистрирован: 18.06.2014

Добрый вечер,

Вот код. 

int val;   
void setup() {
  Serial1.begin(115200);
  Serial.begin(115200);
  Serial1.setTimeout(7);  
}
void loop() {
  if (Serial1.available() > 0)  {
    val = Serial1.parseInt();
    // выполняется код
    Serial.println(val); 
    //Serial1.flush(); - не работает
  }
}

С одного устройсва на плату по Serial1 отправляется значения, на плате выполняется код, и потом отпраляются обратно другие значения на другой Serial порт. Но дело в том,что val принимает только самое первое значение из буфера, получается,что программа работает с запозданием. И выполняет расчеты со всеми значениями из буфера.

Как сделать так,чтобы val посредством parseInt принимал только последнее поступившее значение в буфер?

diger67
Offline
Зарегистрирован: 25.07.2015

Интересно а где вы присвоили val новое значение которое он должен передать, например

void loop() { 
if (Serial1.available() > 0)  { 
val = Serial1.parseInt();    // выполняется код
​//вставить функцию обработки принятой информации. 
if(val  == "I resived OK")
{
  val = myval;
}
Serial.println(val);  
//Serial1.flush(); - не работает 
} 
​

Идея такова что между приемом и отправкой должна стоять Ваша функция обработки принятой информации. иначе получается что отправляется то что принято. для реализации вашей идеи только путем сравнения. Создаете case или if с перечислением сообщений на которые должна быть реакция.

 

PockerFace
Offline
Зарегистрирован: 18.06.2014

Дело не в этом.

Во время работы функции обработки принятой информации, на микроконтроллер поступают ещё значения. И даже если в serial порт перестают поступать данные, функция продолжает работу, пока все значения из буфера не обработает.

Пс забыл упомянуть, что мой код - пример, там показана лишь основная часть...

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Всё логично с точки зрения встроенных классов: пока буфер приёма не переполнился, и пока в буфере есть что-то, похожее на число - он его вам и будет возвращать. В исходниках Stream parseInt юзает peek, который в HardwareSerial возвращает символ из текущей позиции, никуда позицию чтения не сдвигая, в отличие от вызова read, в котором позиция чтения сдвигается.

Вывод: либо переделывать, либо - после parseInt вычитывать весь буфер вызовами read, пока есть available().

PockerFace
Offline
Зарегистрирован: 18.06.2014

Скачал старую версию IDE, вставил Flush() в код, но все равно функция работает с запозданием. Еще пытался поменять parseint на peek - опять та же история. Уже не знаю, что и делать.

Пробовал завершать прием данных с помощью Serial.end(), опять не помогло:

int val; 
int prop = 1;   

void setup() {
  Serial.begin(9600);
  Serial1.setTimeout(4);  
}
void loop() {
  if(prop == 1) {
     Serial1.begin(9600 );
  }
  if (Serial1.available() > 0)  {
     val = Serial1.parseInt();
     delay(1000);
     Serial.println(val);
     Serial1.end();
     prop = 1;
  }
  else {
     prop = 0;
  }
}

 

diger67
Offline
Зарегистрирован: 25.07.2015

Или я ни чего не понимаю или суть такова, parsentInt обрабатывает по 2 байта int = byte * 2; следовательно вы считываете только первую посылку. Посмотрел примеры людей в интернете, там это выглядит так:

void setup() { 
   Serial.begin(9600); 
 } 
 void loop() { 
   // Assumes a string in from the serial port like so: 
   // s ledNumber, brightness \n 
   // for example: "s5,200\n": 
 
 
   int ledNumber = 0; 
   int brightness = 0; 
    
   if (Serial.find("s")) { 
     ledNumber = Serial.parseInt(); // parses numeric characters before the comma 
     brightness = Serial.parseInt();// parses numeric characters after the comma 
      
     // print the results back to the sender: 
     Serial.print("LED number: " ); 
     Serial.print(ledNumber); 
     Serial.print(" at "); 
     Serial.println(brightness); 
 
 
40     // set the LED: 
41     analogWrite(ledNumber, brightness); 
42   } 
43 } 

Следовательно сколько элементов вы передаете, столко раз надо прочитать поток, а значит должно быть столько переменных val n, val n+1. Сколько аргументов вы передаете. Последним будет val end, вот его то и выведя в UART увидите в мониторе последнее принятое значение.

do
{
    val = Serial.parseInt()
}while(Flush()); 
Serial.print(val);

Здесь мы принимаем аргумент за аргументом и при выполнении условия окончания приема выходим с сохранением последнего принятого аргумента, может надо будет попробовать !Flush() для условия while, но это уже ньюансы.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

PockerFace пишет:

Скачал старую версию IDE, вставил Flush() в код, но все равно функция работает с запозданием. Еще пытался поменять parseint на peek - опять та же история. Уже не знаю, что и делать.

Пробовал завершать прием данных с помощью Serial.end(), опять не помогло:

Вы хотя бы осознаёте, что пытаетесь делать? Читаете документацию - хотя бы? peek - оставляет буфер нетронутым, никак не меняя позицию чтения. Serial.end - вообще из другой оперы.

Я же говорю - для того, чтобы вычитать два числа из Serial вызовами parseInt - надо понимать, как этот самый parseInt устроен. Я писал выше, как. Он не меняет позицию чтения из буфера, пока там есть что-то похожее на число. Поэтому с вашей стороны надо предпринять какие-то действия, чтобы дать понять Serial, что нужное вам число вы прочитали. Выше - я писал вариант.

Для танкистов, ещё раз: в буфер упал символ '1'. Его можно преобразовать в число? Можно. Вызов parseInt вернёт вам единичку в виде уже числа, а не символа, оставив эту единичку в буфере. Далее - в буфер падает символ '2', вы вызываете parseInt и - получаете, как и ожидалось, число 12, потому что и '1', и '2' - можно преобразовать в числа 1 и 2, и их вместе - в число 12.

В каком месте моих объяснений я пишу на китайском?

З.Ы. Строго говоря, внутри parseInt есть вызов read, но - тут возникают проблемы, когда в буфере ещё не всё число. Но про это - не будем, да?

 

diger67
Offline
Зарегистрирован: 25.07.2015

DIYMan пишет:

PockerFace пишет:

Скачал старую версию IDE, вставил Flush() в код, но все равно функция работает с запозданием. Еще пытался поменять parseint на peek - опять та же история. Уже не знаю, что и делать.

Пробовал завершать прием данных с помощью Serial.end(), опять не помогло:

Вы хотя бы осознаёте, что пытаетесь делать? Читаете документацию - хотя бы? peek - оставляет буфер нетронутым, никак не меняя позицию чтения. Serial.end - вообще из другой оперы.

Я же говорю - для того, чтобы вычитать два числа из Serial вызовами parseInt - надо понимать, как этот самый parseInt устроен. Я писал выше, как. Он не меняет позицию чтения из буфера, пока там есть что-то похожее на число. Поэтому с вашей стороны надо предпринять какие-то действия, чтобы дать понять Serial, что нужное вам число вы прочитали. Выше - я писал вариант.

Для танкистов, ещё раз: в буфер упал символ '1'. Его можно преобразовать в число? Можно. Вызов parseInt вернёт вам единичку в виде уже числа, а не символа, оставив эту единичку в буфере. Далее - в буфер падает символ '2', вы вызываете parseInt и - получаете, как и ожидалось, число 12, потому что и '1', и '2' - можно преобразовать в числа 1 и 2, и их вместе - в число 12.

В каком месте моих объяснений я пишу на китайском?

 

Дык ЗАПЯТАЯ и является тем самым разделителем что здесь не правильно и не понятно.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

diger67 пишет:

Или я ни чего не понимаю или суть такова, parsentInt обрабатывает по 2 байта int = byte * 2; следовательно вы считываете только первую посылку. Посмотрел примеры людей в интернете, там это выглядит так:

Не увидел, где там по два байта:

long Stream::parseInt(LookaheadMode lookahead, char ignore)
{
  bool isNegative = false;
  long value = 0;
  int c;

  c = peekNextDigit(lookahead, false);
  // ignore non numeric leading characters
  if(c < 0)
    return 0; // zero returned if timeout

  do{
    if(c == ignore)
      ; // ignore this character
    else if(c == '-')
      isNegative = true;
    else if(c >= '0' && c <= '9')        // is c a digit?
      value = value * 10 + c - '0';
    read();  // consume the character we got with peek
    c = timedPeek();
  }
  while( (c >= '0' && c <= '9') || c == ignore );

  if(isNegative)
    value = -value;
  return value;
}

Читает, пока есть данные в буфере приёма и пока не уткнётся в неподходящий символ.

diger67
Offline
Зарегистрирован: 25.07.2015

Я потрудился почитать как все это должно работать, посмотрел как умные люди используют данные функци и попытался как понимаю ему объяснить как это можно примерно реализавать. Может тоже не прав. Хотя вижу это все в первый раз.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Вот лень компилировать и проверять, но навскидку, без всяких parseInt и прочей шелухи. Считаем, что команда окончена, когда идёт символ новой строки:

String receiveBuff;

void INTValueReceived(int val)
{
Serial.print("Получили число из UART:");
Serial.println(val);
}
void ParseLine(const String& delimiter)
{
   int idx;
   while(1)
   {
     idx = receiveBuff.indexOf(delimiter);
    if(idx == -1) break;
    int val = receiveBuff.substring(0,idx).toInt();
    receiveBuff = receiveBuff.substring(idx+1 + delimiter.length());
     INTValueReceived(val);
   }

receiveBuff = "";
}

void loop()
{
   while(Serial.available())
{
 char ch = Serial.read();
if(ch == '\r')
 continue;
if(ch == '\n')
   ParseLine(",");
else
  receiveBuff += ch; 
}
}

Делов на три минуты. Числа посылать, разделяя их запятой, например. Или - любым другим символом или строкой, см.

ParseLine(",");

 

diger67
Offline
Зарегистрирован: 25.07.2015

DIYMan пишет:

Не увидел, где там по два байта:

Я так понял что эта фунция может скушать максимально 0XFFFF. Т.е. преобразовать полученный аргумент в число от 0 до 65535. Значит значение аргумента должно лежать в этих пределах. Как то так....

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

В моём коде есть недоработка, лень дописывать, кому надо - увидит.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

diger67 пишет:

DIYMan пишет:

Не увидел, где там по два байта:

Я так понял что эта фунция может скушать максимально 0XFFFF. Т.е. преобразовать полученный аргумент в число от 0 до 65535. Значит значение аргумента должно лежать в этих пределах. Как то так....

Ей наплевать на переполнение, посмотрите, наконец, кусок кода из исходников! Читает, пока символы есть в буфере, вываливается по таймауту, или когда встретит символ, который нельзя сконвертировать в число. И тупо накапливает число:

 else if(c >= '0' && c <= '9')        // is c a digit?
      value = value * 10 + c - '0';

ГДЕ тут два байта ограничение?

Yarik.Yar
Offline
Зарегистрирован: 07.09.2014

Холивар прекращаем, господа, достаём огнетушители и яростно поливаемся! 

PockerFace
Offline
Зарегистрирован: 18.06.2014

Спасибо большое за ответы.

Почитал документацию...

DIYMan, в вашем коде используется read, а он также берет только самый первый в очереде символ, есть ли аналог read, который мог принимать только последнее поступившее значение из буфера?

По сути ваш код и этот похожи :?


String inString = "";

void setup() {
  Serial1.begin(9600);
  Serial.begin(115200);

  Serial1.println("\n\nString toInt():");
  Serial1.println();
}

void loop() {
  while (Serial1.available() > 0) {
    Serial1.available();
    int inChar = Serial1.read();
    if (isDigit(inChar)) {
      inString += (char)inChar;
    }
    if (inChar == '\n') {
      Serial.print("Value:");
      Serial.println(inString.toInt());
      inString = "";
    }
  }
}

P.s. прошу прощения,если что-то не понял

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

PockerFace пишет:

Спасибо большое за ответы.

Почитал документацию...

DIYMan, в вашем коде используется read, а он также берет только самый первый в очереде символ, есть ли аналог read, который мог принимать только последнее поступившее значение из буфера?

Вызов read меняет позицию чтения из буфера, так что всё будет нормально. Почему лучше не использовать parseInt - я писал выше, на примере того, когда в буфер упала ещё не вся строка, переданная ардуине.

 

PockerFace
Offline
Зарегистрирован: 18.06.2014

Если после if (inChar == '\n') {

вставить delay(1000);, и отправить на плату большое кол-во значений, то код выше выводит только все прешедшие за паузу значения, а не последнее. Получается, отправил 40 значение, и потом все 40 сек будет их присылать, а не только последнее прешедшее за паузу.

T.Rook
Offline
Зарегистрирован: 05.03.2016

Если Вам нужен только последний символ, ну и вычитывайте всё в никуда, оставляя только последний:


String inString = "";

void setup() {
  Serial1.begin(9600);
  Serial.begin(115200);

  Serial1.println("\n\nString toInt():");
  Serial1.println();
}

void loop() {
  while (Serial1.available() > 0) {
    int inChar = Serial1.read(); 
    }
   //делаем что нибудь с одним последним символом inChar
  }
}

хотите последнее число:

String inString = "";

void setup() {
  Serial1.begin(9600);
  Serial.begin(115200);

  Serial1.println("\n\nString toInt():");
  Serial1.println();
}

void loop() {
  while (Serial1.available() > 0) {
    Serial1.available();
    int inChar = Serial1.read();
    if (isDigit(inChar)) {
      inString += (char)inChar;
    }
    else 
     if (inChar == '\n') {
      Serial.print("Value:");
      Serial.println(inString.toInt());
      inString = "";
    }
    else inString="";
  }
}

upd:  с числом я явно нахомутал :) но надеюсь идея понятна

PockerFace
Offline
Зарегистрирован: 18.06.2014

Спасибо, все понятно

maxnnovik
Offline
Зарегистрирован: 17.12.2016

А  я для такого очищения буфера дописал ещё одну строчку считывания порта. При этом той переменной нигде не использую и всё заработало. Специально для того что бы понять в чём ошибка даже вывод результата ввода на экран делал. Приэтом если переменную вводить не char а int ошибка усугубляется. Вот код

       if(Serial.available() > 0)
       {
       char rtD = Serial.read();
       Serial.println(rtD);
        if(rtD >1)
         {
          digitalWrite(3, HIGH);
         }
        delay(20000);
        digitalWrite(3, LOW);
        char rtB = Serial.read();
       }
 
 Именно char rtB = Serial.read(); чистит буфер.