serial.readbytes() not behaving as expected, what is going on?

char buffer[4];

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

void loop() {
  if (Serial.available()) {
    Serial.readBytes(buffer, 3);
    Serial.println(buffer);
  }
}

if I type in several characters into the serial monitor and hit enter the code spits out the first three characters during the first loop, but then when it runs again it spits out the next three, or the last three,

for example I type in "tomcat" it will print "tom", then "cat", then a blank line and then "t".

I expect it to simply give me "tom" and then load the buffer again.

I am trying to understand this function and I'm not finding much on line that tells me much. Maybe I don't know where to look. Google is not helping me much today!

zero termination is at position 3 (you do not set it, so you are lucky it is there). the blank line is "/r/n" a new line from Serial Monitor. and the t is a leftover in buffer on position 2.

I guessed that the blank line was coming from the Serial Monitor, but what I don't understand is why it keeps reading out of the buffer.

I even tried a line to clear the buffer: while(Serial.available()) Serial.read();

but that did not seem to be the solution.

char buffer[4];

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

void loop() {
  if (Serial.available()) {
    Serial.readBytes(buffer, 3);
    Serial.println(buffer);
    while(Serial.available())Serial.read();
  }
}

Start here

a string must be "zero terminated"

char buffer[4];

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

void loop() {
  if (Serial.available()) {
    int l = Serial.readBytes(buffer, 3);
    buffer[l] = 0;
    Serial.println(buffer);
  }
}

Juraj, that works better, but still results in the second loop giving me the second set of three characters. I want it to stop and wait until I enter more characters. I don't understand what setting "buffer[l] = 0; " does.

I have previously found the link posted by AWOL and I guess I will study it again.

it is called a zero terminated string. the print function and other string functions need to know where your text ends.

I want it to stop and wait until I enter more characters.

YOU need to be smarter about what you type, then. If you don't want the Arduino to deal with the "cat" portion, when you send "tomcat", then don't send the "cat" portion.

Serial data arrives slowly, from the Arduino's point of view. It can't tell how many characters you expect it to deal with, when you send more than what you want it to deal with, unless you give it a clue.

Thanks for the replies. I am not sure what serial.readbytes() is good for then.

PaulS, when I type just "cat" it prints "cat", then a blank line, and then a "t". I don't understand why or how to get the behavior that I want. I do understand that a string must be terminated with a null, and it appears that serial.readbyes() fails to do that.

I'll keep reading what I can find and keep studying examples. I am not finding any good resources for really learning code. Most books seem to specialize in practical examples and that makes sense, but I am at the point where I want to know more. I have built a lot of projects, my house has quite a few running Arduino's in it now, but I'm struggling with serial communications. Yeah, I can use other people's code, but I'd like to understand it better.

It seems difficult to bridge the knowledge gap between the basics and advanced skills.

Thanks for your help.

I do understand that a string must be terminated with a null, and it appears that serial.readbyes() fails to do that.

It "fails" to do that because the function is NOT intended to return a string.

Take a look at your last reply. Can you see where one word ends and another begins? What is the key?

Can you see where one sentence ends and another begins? What is the key?

Can you see where one paragraph ends and another begins? What is the key?

If you said, in all three cases, that the key is a delimiter, you'd be correct. Of course, there are three (or more) different delimiters involved.

Robust serial communication relies on delimiters. The carriage return and/or line feed that the serial monitor app adds are potentially useful delimiters.

Read three bytes and do something with them. Then, read and discard the data until the carriage return and/or line feed arrives.

Here is what I came up with:

char buffer[4];

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

void loop() {
  if (Serial.available()) {
    int i = Serial.readBytes(buffer, 3);
    Serial.println(buffer);
    delay(1);
    while (Serial.available())Serial.read();
  }
}

I'm not quite sure what is happening, other than data is still coming in. Adding a delay allows the data to arrive, then the while statement reads it out. Now I am getting the kind of results that I was expecting.

amdkt7:
Here is what I came up with:

Have you tried the examples (probably the 2nd example) in Serial Input Basics - the link you were given in Reply #3? They are simple reliable ways to receive data.

...R

I am currently playing with that very example. It is a good method I can see, it's non blocking, but I have been trying to understand why serial.readbytes(0) does what it does. I have figured out a way to get what I expected out of it. I don't know how much difference it makes in the amount of code, variable space, memory usage. I do know that it takes me less time to write the code.

I know that serial.readbytes() is blocking, but most of my needs that's not an issue yet.

My question continues to be unanswered, I was hoping for a good explanation for how serial.readbytes() works and how to use it. Yes, I got some good leads on other ways to do things, and I did figure out how to get it working anyway.

Thanks again for any assistance at all

amdkt7:
My question continues to be unanswered, I was hoping for a good explanation for how serial.readbytes() works

You could look at the source code for readBytes() in Stream.cpp.

Thanks, I was looking in the wrong place. I think I looked at the Stream.h file and it made no sense to me. This is what I found.

// read characters from stream into buffer
// terminates if length characters have been read, or timeout (see setTimeout)
// returns the number of characters placed in the buffer
// the buffer is NOT null terminated.
//
size_t Stream::readBytes(char *buffer, size_t length)
{
  size_t count = 0;
  while (count < length) {
    int c = timedRead();
    if (c < 0) break;
    *buffer++ = (char)c;
    count++;
  }
  return count;
}

I'm still trying to understand it, I forget what the * is used for.
Thanks again!

amdkt7:
I'm still trying to understand it, I forget what the * is used for.

Dereferencing a pointer. In this case: buffer.
http://www.cplusplus.com/doc/tutorial/pointers/

You are still treating buffer as a string. It is NOT a string, and you should NOT be passing it to functions that expect strings.

A string is a NULL terminated array of chars. What you have is NOT NULL terminated, so it is NOT a string.

gfvalvo: I will attempt to read that later, I remember finding that once before, it's a little hard at my newbie level. I'm pretty tired!

PaulS: Thanks, I understand what you are saying. I could add a line to add a null to whatever you would call that which is not a string. :slight_smile:

I could add a line to add a null to whatever you would call that which is not a string.

You should. "That which is not a string" is a char array.