Arduino Serial.read question

Slightly confused over Serial.read - can someone help me understand?

Simple code snippet.

while (Serial.available()==0){ // Loop forever waiting for serial port input
}
//Key has been pressed
for (int i=0; i<=10; i++){
int s=Serial.available();
int r=Serial.read();
Serial.println(i);
Serial.println (s);
Serial.println(r);
}

I type in 'a' and press the send button in serial console.
This results in the following:

i s r
0 1 97
1 0 -1
2 0 -1
3 0 -1
4 1 10
5 0 -1
6 0 -1
7 0 -1
8 0 -1
9 0 -1
10 0 -1

Why the gap between the 'a' that I entered and the '/n' ?
Is this just some internal delay in moving data around or am I missing something?

I would have expected

0 1 97
1 1 10
2 0 -1
3 0 -1
etc....

Do I simply need a delay between serial.reads in order to read both the 97 and the /n or do I need a specific number of reads (5) to capture both 97 and /n?

You should wait until the required/expected number of characters is available. Reading the input buffer is so much faster than the transmission over the line.

I would take a different approach, create a function that handles lines and use a
function to collect lines in a buffer and then call that function.

const byte maxMsgLen = 16;

const byte cbLF = 10;
const byte cbCR = 13;

void setup() {
  Serial.begin(250000);
  Serial.println(F("echo lines"));
}

void loop() {
  handleSerial();
}

void oneLineReceived(const char * buffer) {
  Serial.println(buffer);
}

void handleSerial() {
  static uint8_t bIndex;
  static uint8_t buffer[maxMsgLen + 1];
  bool lineReady = false;
  if (Serial.available()) {
    uint8_t inChar = Serial.read();
    if (inChar != cbLF) {
      if (inChar == cbCR) {
        lineReady = true;
      } else {
        buffer[bIndex++] = inChar;
        lineReady = (bIndex == maxMsgLen);
      }
      if (lineReady) {
        buffer[bIndex] = 0;
        oneLineReceived((const char*)buffer);
        bIndex = 0;
      }
    }
  }
}

In other words, you check if there is/are one or more characters available and next you read 10 regardless. The -1 indicates that there was no characcter available at the time of reading.

One solution:

if(Serial.available() >=10)
{
  // read 10 bytes
  ...
  ...
}

You can get some other ideas from Serial Input Basics - updated

You are missing that serial is super slow compared to what the processor can do. In between one byte and the next the processor can make a cup of tea and relax with it's favourite novel for a while before checking to see if there's another byte. Any well written code for handing serial input will only see at most 1 or a very small number of bytes, mostly it will not see any, each time it checks, you have to write code to assemble those individual bytes in to whatever it was you were trying to get.

The tutorial @sterretje linked to is very good, I suggest you read it.

1 Like

That makes sense.

If I put a delay in

while (Serial.available()==0){ // Loop forver waiting for serial port input
}
//Key has been pressed
for (int i=0; i<=10; i++){
int s=Serial.available();
int r=Serial.read();
Serial.println(i);
Serial.println (s);
Serial.println(r);
Serial.println();
delay(100);
}

Now it behaves as I would expect:

0 1 97
1 1 10
2 0 -1
etc....

How slow is your serial line?
:open_mouth:

1 Like

That's a bit complex, but should be bomb-proof - thanks.

It's not really complicated if you look close up.

It is encapsulated in a function hiding the 'complexity' and does not slow your sketch down.

Yes, I can see that now.

The code moves on as soon as the 'a' is received and loops like crazy while the /n is being received.

It's a single character that I need from the user - but of course the user may put in a string of characters....

The best approach is to look for the /n before stepping on.

That way, the user can input any number of characters, but the code will not step on until it reads the /n character, at which point any future reads of Serial.available should read 0 and serial.read should read -1

I will code that up and test it.

Yes, having read through it in more detail it's not that complex.

One silly question - what does this do?

It prints "echo lines" on the serial monitor, followed by a newline.

1 Like

Thanks all - I now have a good understanding of how it works and what to do
Lovin' the forum - so many enthusiastic people out there ready to help!

On with the coding and learning!

And the F() macro keeps the string literal only in flash memory,
standard string literals consume flash and RAM on an AVR.

I don't know if this is the right time to mention this but don't get too hooked on using delay(), it will fix small problems like this now, but as soon as you want your code to do more than one thing at a time you will need a more advanced strategy for writing code. Delay stops anything else from happening and is much debated on here.

Here is a slightly different example sketch from my tutorial Arduino Software Solutions
change the
if (readStringUntil(input, terminatingChar, 20)) to
if (readStringUntil(input, terminatingChar, 10)) if you want to only read 10 chars

// readStringUntilLimited.ino
// Reads chars into a String until newline '\n'
// https://www.forward.com.au/pfod/ArduinoProgramming/SoftwareSolutions/index.html
// Pros: Simple. Non-Blocking, more Robust against unexpectedly long input lines
// Cons: Does not return short lines until the until_c char is found. i.e. no timeout

String input;

void setup() {
  Serial.begin(9600);
  for (int i = 10; i > 0; i--) {
    Serial.print(' '); Serial.print(i);
    delay(500);
  }
  Serial.println();
  Serial.println(F("readStringUntilLimited.ino"));
  input.reserve(20); // expected line size, see Taming Arduino Strings
  // https://www.forward.com.au/pfod/ArduinoProgramming/ArduinoStrings/index.html
}

// read Serial until until_c char found or limit char read, returns true when found/limited else false
// non-blocking, until_c is returned as last char in String, updates input String with chars read
bool readStringUntil(String& input, char until_c, size_t char_limit) {
  while (Serial.available()) {
    char c = Serial.read();
    input += c;
    if (c == until_c) {
      return true;
    }
    if (input.length() >= char_limit) {
      return true;
    }
  }
  return false;
}

char terminatingChar = '\n';
void loop() {
  if (readStringUntil(input, terminatingChar, 20)) { // read until find newline or have read 20 chars
    if (input.lastIndexOf(terminatingChar) >= 0) {   // could also use check  if (input[input.length()-1] == terminatingChar) {
      Serial.print(F(" got a line of input '")); Serial.print(input); Serial.println("'");
    } else {
      Serial.print(F(" reached limit without newline '")); Serial.print(input); Serial.println("'");
    }
    input = ""; // clear after processing for next line
  }
}

Here is a simple way to flush any old input on start up (also in the tutorial)

Serial.setTimeout(100); // set 100mS timeout 
Serial.readString();
Serial.setTimeout(1000); // reset default timeout