Pages: [1]   Go Down
Author Topic: How to clean up Serial port buffer  (Read 4992 times)
0 Members and 2 Guests are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 2
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
#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:
"duck" [enter]
"s" [enter]
And I got following output:
Code:
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/
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 474
Posts: 18696
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Personally I wouldn't use serial events.

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

Offline Offline
Edison Member
*
Karma: 26
Posts: 1339
You do some programming to solve a problem, and some to solve it in a particular language. (CC2)
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
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:
char ch = '\n\r';

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

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

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

Offline Offline
Edison Member
*
Karma: 26
Posts: 1339
You do some programming to solve a problem, and some to solve it in a particular language. (CC2)
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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 ?
Logged

Copenhagen, Denmark
Offline Offline
Edison Member
*
Karma: 32
Posts: 1206
Have you testrun your INO file today?
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Massachusetts, USA
Offline Offline
Tesla Member
***
Karma: 203
Posts: 8720
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Your problem is that:
Code:
strcpy(buff, "");
just puts a '\0' in the first position of the buffer.
Code:
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:
int length = Serial.readBytesUntil('\n',buf, MAX);
if (length > 0)
    buff[length] = '\0';
else
    Serial.println("No valid data found");
Logged

Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 474
Posts: 18696
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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().

Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 2
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
strcpy(buff, "");
just puts a '\0' in the first position of the buffer.
Logged

Offline Offline
Edison Member
*
Karma: 26
Posts: 1339
You do some programming to solve a problem, and some to solve it in a particular language. (CC2)
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Convincing... Thank you for the thorough response.

Edit: perhaps the serialEvent() reference page should tell a bit more...
« Last Edit: October 07, 2012, 06:07:07 pm by tuxduino » Logged

Offline Offline
Edison Member
*
Karma: 26
Posts: 1339
You do some programming to solve a problem, and some to solve it in a particular language. (CC2)
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48569
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 474
Posts: 18696
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Exactly. It's a kludge. If you want to do something every time you go through loop, just do it.
Logged

Offline Offline
Edison Member
*
Karma: 26
Posts: 1339
You do some programming to solve a problem, and some to solve it in a particular language. (CC2)
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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 smiley-razz Thanks for pointing that out smiley

Code:
#include <Arduino.h>

int main(void)
{
    init();

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

    setup();

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

    return 0;
}
Logged

Copenhagen, Denmark
Offline Offline
Edison Member
*
Karma: 32
Posts: 1206
Have you testrun your INO file today?
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

serialEvent() is not an interrupt handler. Look inside hardware/arduino/cores/arduino/HardwareSerial.cpp
When I get the time. smiley If it just gets called before entering the loop() again... damn, I'll just have to look now
click, click, editor-woosh
Code:
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.
Logged

Offline Offline
Edison Member
*
Karma: 26
Posts: 1339
You do some programming to solve a problem, and some to solve it in a particular language. (CC2)
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Pages: [1]   Go Up
Jump to: