Go Down

Topic: How to clean up Serial port buffer (Read 5906 times) previous topic - next topic

freezing_point

Hi all,
I try to use serialEvent() and Serial.readBytesUntil() functions to catch-up commands from the user. But i believe that avr serial port register is left dirty when i am exiting from the serialEvent(). Documentationhttp://arduino.cc/en/Reference/SerialEvent says to to use Serial.read() to capture data from the register, so is it big no no to use Serial.readBytesUntil()? I tried to use Serial.flush() but it doesn't help. What is the right way to do this?

Here is the code:
Code: [Select]

#include <string.h>

#define MAX 64 // Arduino can hold up to 64 bytes

char buf[MAX];
char pr[MAX];

void setup()
{

Serial.begin(115200);
Serial.setTimeout(10000);
Serial.println("Arduino up and running");

}


void serialEvent(){
strcpy(buf, ""); /* Clear the buffer */
/* read the serial rx buffer to global variable */
if(Serial.readBytesUntil('\n\r',buf, MAX) == 0){  */
Serial.println("No valid data found");

}}



void loop()
{
   
    if(strlen(buf) > 0){
    Serial.println(strcpy(pr,buf));
    Serial.println(strlen(buf));
    strcpy(buf, "");
    Serial.println(strlen(buf));
    }
    Serial.println("in main loop");
    Serial.print(buf);  Serial.print(" --- "); Serial.println(pr);
   
    delay(3000);
}


reproduce the bug:
Code: [Select]

"duck" [enter]
"s" [enter]

And I got following output:
Code: [Select]

Arduino up and running
in main loop
---
in main loop
---
duck
4
0
in main loop
--- duck
in main loop
--- duck
suck
4
0
in main loop
--- suck
in main loop
--- suck


/mikko/

Nick Gammon

Personally I wouldn't use serial events.

http://www.gammon.com.au/serial
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

tuxduino

Code: [Select]
Serial.readBytesUntil('\n\r',buf, MAX)

A single char is enclosed in single quotes. Two or more chars make a string, which must be included in double quotes.

I tried compiling this simple test sketch with verbose compilation enabled:

Code: [Select]

char ch = '\n\r';

void setup() {}
void loop() {}


I got this (buried inside a flood of other messages):

Code: [Select]

sketch_oct07a.cpp:4:11: warning: multi-character character constant
sketch_oct07a.cpp:4:11: warning: overflow in implicit constant conversion

tuxduino


Personally I wouldn't use serial events.

http://www.gammon.com.au/serial


Why ? After all, implementing serialEvent() is equivalent to write if (Serial.available() > 0) { ... }
The three dots are what would go into serialEvent().

Or am I missing something ?

Msquare

I am sorry, I do not get the purpose of your code - even allowing for it to be a demo/test of a percieved error.

There are two ways of reading Serial data.

The first is to occasionally call Serial.available(), and call Serial.read() to handle the character if it has arrived. If the Serial managed to receive 3 characters between your calling Serial.available() then the 2nd and 3rd byte will still be there on your next check & read call. If you expected 5 bytes, and only 3 arrived .. do something else until calling available again to see if they have arrived.

The other way, is the Serial.event(), which looks to me like an interrupt routine. (Admittedly never used it - never had a need) As it gets called (interrupting what your program was doing) immediatly after a byte is received, there will only be ONE byte to read. Doing Serial.readBytesUntil is going to confuse/break everything.

Unless you are truly confident/knowledgeble with handling interrupts, then stick with the above polling technique.

I have never heard of the AVR serial "getting dirty". And no, you have not discovered a new an exquiste subtle bug.

johnwasser

Your problem is that:
Code: [Select]
strcpy(buff, "");
just puts a '\0' in the first position of the buffer.
Code: [Select]
Serial.readBytesUntil('\n\r',buf, MAX);
reads the 's' and puts it in the first position of the buffer.  IT DOES NOT ADD A NULL TERMINATOR.  If you want the string to end after the 's' you have to put in your own terminator.
Code: [Select]

int length = Serial.readBytesUntil('\n',buf, MAX);
if (length > 0)
    buff[length] = '\0';
else
    Serial.println("No valid data found");
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

Nick Gammon


Why ? After all, implementing serialEvent() is equivalent to write if (Serial.available() > 0) { ... }
The three dots are what would go into serialEvent().


I think it's just obscure. Why not make it explicit?

Code: [Select]

void loop ()
  {
  if (Serial.available ())
     processOneByte ();

  // other stuff
  }


Then the OP starts making the serialEvent call wait for more than one character, which hides the fact that s/he has blocked what loop is doing.

And we get this confusion:

Quote
But i believe that avr serial port register is left dirty when i am exiting from the serialEvent().


Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

freezing_point

indeed, this is the case! And also realized that is not very efficient to wait '\n' inside interrupt handler. so maybe i re-think this little further when get some sleep....

thanks all
/mikko/


Your problem is that:
Code: [Select]
strcpy(buff, "");
just puts a '\0' in the first position of the buffer.

tuxduino

#8
Oct 08, 2012, 12:45 am Last Edit: Oct 08, 2012, 01:07 am by tuxduino Reason: 1
Convincing... Thank you for the thorough response.

Edit: perhaps the serialEvent() reference page should tell a bit more...

tuxduino

serialEvent() is not an interrupt handler. Look inside hardware/arduino/cores/arduino/HardwareSerial.cpp

The ISR just stores the received char into the circular buffer. serialEvent() is called whenever Serial.available() > 0.

PaulS

Quote
serialEvent() is called whenever Serial.available() > 0.

No. The serialEvent() function is called at the end of loop(), if Serial.available() returns greater than 0.

Nick Gammon

Exactly. It's a kludge. If you want to do something every time you go through loop, just do it.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

tuxduino


Quote
serialEvent() is called whenever Serial.available() > 0.

No. The serialEvent() function is called at the end of loop(), if Serial.available() returns greater than 0.


Of course :P Thanks for pointing that out :)

Code: [Select]

#include <Arduino.h>

int main(void)
{
    init();

#if defined(USBCON)
    USBDevice.attach();
#endif

    setup();

    for (;;) {
        loop();
        if (serialEventRun) serialEventRun();
    }

    return 0;
}

Msquare


serialEvent() is not an interrupt handler. Look inside hardware/arduino/cores/arduino/HardwareSerial.cpp
When I get the time. :) If it just gets called before entering the loop() again... damn, I'll just have to look now
click, click, editor-woosh
Code: [Select]
for (;;) {
  loop();
  if (serialEventRun) serialEventRun();
  }
OK, so you could get called in the Serial.serialEvent routine with loads of characters, if your loop is full of delays - like the OP's 3 second.

tuxduino

Quote
OK, so you could get called in the Serial.serialEvent routine with loads of characters,


Usually* not more than 64, which is the size of Serial ring buffer.


* unless you tweak HardwareSerial.ccp source code.

Go Up