What is happening in this serial code??

I've been working on the arduino to read strings for the past 2.5 hours and I think I'm getting close. I'm trying to understand how the arduino processes the serial data and I've concluded that it gets stored in the serial buffer and then processed one character at a time. But what I'm wondering is why Serial.available returns extremely odd numbers when I use a while loop and an if statement (please compile this for yourself once using a while loop and once an if statement and use 10+ characters for input). I've also concluded that Serial.read reads the first number in the serial buffer. So what does Serial.available return and I'm also wondering how to get the arduino to wait for you to press enter before putting everything in the buffer.

I'm sorry if that's obscure but try to answer it the best you can please

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  while (Serial.available() > 0)
  {
    Serial.print(Serial.available(), DEC);
    Serial.print('-');
    Serial.print(Serial.read(), BYTE);
    Serial.print('\n');
  }
}

Could you post your results?

I have no Arduino in reach.
:slight_smile:

Google helped me figure it out... The atmega328 was processing the data faster than the serial line could send it to the serial buffer so it was correctly telling me how many items were in the buffer, it's just that they hadn't all be sent yet. By adding a slight delay, that gave the serial line plenty of time to put in those last few characters. The reason for the differences in the if and while was the fact that the if had to loop back through the entire function (much slower) whereas the while could just run through that small part of code quickly (much faster).

So in all, just add a delay to allow all the characters to get into the buffer before it starts processing them

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  boolean bufferFill = 0; /*Boolean that states whether the serial buffer has been filled*/
  while (Serial.available() > 0)
  {
    if (bufferFill == 0) /*Checks to see if the buffer has been filled*/
    {
      delay(50); /*The delay (might need to be higher for more characters*/
      bufferFill = 1; /*States that the buffer has been filled*/
    }
    Serial.print(Serial.available(), DEC); /*Prints the amount of bytes in the serial buffer*/
    Serial.print('-');
    Serial.print(Serial.read(), BYTE) ;/*Prints the first byte of the serial buffer (then probably deletes it or something)*/
    Serial.print('\n');

  }
}

So what does Serial.available return

Serial.available() returns the number of characters currently in the input buffer. As bytes are put into the buffer, by a source external to the Arduino, this number increases. As Serial.read() removes bytes from the buffer, this number decreases.

and I'm also wondering how to get the arduino to wait for you to press enter before putting everything in the buffer.

If you're referring to the Serial.read() input buffer, the Arduino itself (hardware, that is) does not put things into that buffer. If you mean the serial monitor in the Arduino IDE, running on a PC, which does put data into the buffer, you don't have any options that let you control when it sends the data to the Arduino hardware. I'm not entirely sure I understood the second question correctly, but I hope this helps.

[edit]Glad to see you got it sorted.[/edit]

For completeness I'll post the erroneous results. These results happen to be the same but different numbers of letters will show the differences between the 2

With a while loop...

1-a
2-b
5-c
8-d
8-e
7-f
6-g
5-h
4-i
3-j
2-k
1-l

With an if statement

1-a
2-b
5-c
8-d
8-e
7-f
6-g
5-h
4-i
3-j
2-k
1-l

When I say
"I I'm also wondering how to get the arduino to wait for you to press enter before putting everything in the buffer"
I mean like how the IDE waits to send everything so that it uses the buffer but like putty would send each letter individually so that you couldn't rely on the amount of bytes in the buffer in your code. How can I send stringlike sets of characters? Or sets of characters terminated by a key like the enter key? like "if (Serial.read == '^m')" (or whatever the enter key is)

I think I understand now. People use a variety of third-party terminal emulators on the PC side to do that job. Also, check out the thread at http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1253472652 for a recent discussion about upgrading the Arduino IDE’s serial monitor. It may eventually remove the need for you to use a separate terminal emulator to get the ‘^m’ EOL signal.

Here is a sketch that reads characters into a string without using delay(). You call read_line() inside your loop. If a character is available, it puts it into the string. If you fill up the string, or the user types CR (carriage return, which is what most terminal programs send when you hit the ENTER key), it sets ‘eol’ to true.

Since read_line() does not wait for a character, it returns fairly quickly, so your loop can do other stuff while you’re waiting for user input.

I haven’t thoroughly tested this code, so please let me know if you find any errors.

#define MAX_LINE 20

void
setup()
{
      Serial.begin(9600);
}

void
loop()
{
    boolean eol = false;
    char line[MAX_LINE + 1];
    
    for (;;)
      {
      read_line(MAX_LINE,line,&eol);
      if (eol)
        {
        Serial.write(line);       // echo back the line we just read
        Serial.write("\r\n");
        eol = false;               // get ready for another line
        }
      }
}

void
read_line(int max_line,char *line,boolean *eol)
{
      int c;
      static int line_idx = 0;
      
      if (max_line <= 0)    // handle bad values for max_line
        {
        *eol = true;
        if (max_line == 0)
          line[0] = '\0';
        }
      else                // valid max_line
        {
        if (Serial.available() > 0)
          {
          c = Serial.read();
          if (c != -1)  // got a char -- should always be true
            {
            if (c == '\r')
              *eol = true;
            else
              line[line_idx++] = c;
            if (line_idx >= max_line)
              *eol = true;
            line[line_idx] = '\0';     // always terminate line, even if unfinished
            if (*eol)
              line_idx = 0;           // reset for next line 
            }
          }
        }
}

Regards,

-Mike

[…]so your loop can do other stuff while you’re waiting for user input.

This is a serious nitpick, but I think you should be careful to say that loop can do things, when parts of it is an indefinite loop.

Additionally, I find that using the least amount of pointers tend to generate less questions and problems, in Arduinoworld.

So, I changed the code a bit. (loop can do things other than waiting, and lesser use of pointers)

[UNTESTED CODE]

//heavily based on http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1257731439/7#7
#define MAX_LINE 20

char line[MAX_LINE + 1];

void setup()
{
      Serial.begin(9600);
}

void loop()
{    
      //do something other than waiting for serial transfer
      if (lineAvailable(MAX_LINE,linel))
      {
        Serial.write(line);       // echo back the line we just read
        Serial.write("\r\n");
        eol = false;               // get ready for another line
      }
      //do something other than waiting for serial transfer
}

boolean lineAvailable(int max_line,char *line)
{
      int c;
      static int line_idx = 0;
      boolean eol = false;
      if (max_line <= 0)    // handle bad values for max_line
      {
        eol = true;
        if (max_line == 0)
          line[0] = '\0';
      }
      else                // valid max_line
      {
        if (Serial.available() > 0)
        {
          c = Serial.read();
          if (c != -1)  // got a char -- should always be true
          {
            if (c == '\r')
              eol = true;
            else
              line[line_idx++] = c;
            if (line_idx >= max_line)
              eol = true;
            line[line_idx] = '\0';     // always terminate line, even if unfinished
            if (eol)
              line_idx = 0;           // reset for next line
            }
          }
        }
        return eol;
}

Happy Coding!

AlphaBeta,

I like your version. I agree about pointers being confusing.

I should have been more clear when I said loop -- I was referring to the for (;:wink: loop. Putting a for (;:wink: loop inside the loop() function saves a few nanoseconds since loop() isn't called over and over again. Also, local variables inside loop() are preserved. Of course, you can achieve the same result by putting your for (;:wink: loop at the end of setup(). That's not the Arduino way, just a quirk of mine.

I should also note that even though lineAvailable() is designed to get a line of serial input without waiting, it also makes it easy to wait for a line:

    while (!lineAvailable(MAXLINE,line))
      ;

Regards,

-Mike

this has been a helpful thread.

i tested the code in post #8 above. I fixed the eol variable being called out of scope.

#define MAX_LINE 20

char line[MAX_LINE + 1];

void setup()
{
Serial.begin(9600);
}

void loop()
{
if (lineAvailable(MAX_LINE,line))
{
Serial.write(line); // echo back the line we just read
Serial.write("\r\n");
}
//do something other than waiting for serial transfer
}

boolean lineAvailable(int max_line,char *line)
{
int c;
static int line_idx = 0;
static boolean eol = false;
if (max_line <= 0) // handle bad values for max_line
{
eol = true;
if (max_line == 0)
line[0] = ‘\0’;
}
else // valid max_line
{
if (Serial.available() > 0)
{
c = Serial.read();
if (c != -1) // got a char – should always be true
{
if (c == ‘\r’ || c == ‘\n’)
eol = true;
else
line[line_idx++] = c;
if (line_idx >= max_line)
eol = true;
line[line_idx] = ‘\0’; // always terminate line, even if unfinished
}
if (eol)
{
line_idx = 0; // reset for next line
eol = false; // get ready for another line
return true;
}
else
return false;
}
}
}

sorry about the messed up indentions.