Serial.readStringUntil() -- '\n' v. '\r'

I have a curious problem. For the code below I’d enter a String of length 3 (“hit”), use Serial.readStringUntil(’\n’), and get the output shown - a length of 4, with the last char being 13 (’\r’)

  if(Serial.available()){
    const char * str = Serial.readStringUntil('\n').c_str();
    Serial.printlnf("You said %s, which has a length of %d\n", str, strlen(str));
    Serial.printlnf("The last char has an ascii value of %d\n", str[3]);
    parseNotes(str);
  }

// elsewhere in the code

void parseNotes(const char *c_str)
{
  unsigned int j = 0;
  while (j < strlen(c_str))
  {
    Serial.printlnf("Behold, %c\n", c_str[j++]);
  }
}

The output (using screen /dev/tty.usbmodem12345) looks like this, when entering the word hit and pressing return:

, which has a length of 4

The last char has an ascii value of 13

Behold, h

Behold, i

Behold, t

Behold, 

So I’m off by one, and changing to ‘\r’ solved my problem, but raised two questions:

  1. Why doesn’t ‘\n’ work? I’m doing this on a Mac, I thought Macs always use ‘\n’.
  2. Some of my students use Windows - what should I tell them to use as the end-of-line delimiter?

Thanks for any insights

Hi,
can this be of help?

I believe you're confusing two things:

What character code does pressing the enter key yield?
What line ending is used on which platforms?
As far as I know, the answer to #1 is 13 (ASCII carriage return, "\r")
for all platforms.

Do you have a platform where this is not what you're seeing?

and:

They're different characters. \r is carriage return, and \n is line feed.

cheers!

One additional point of interest may be that the line ending is not (solely) produced by the hardware key you hit but (also) by the terminal program you use to translate that key stroke into bytes to be sent.
In many programs you can decide which line ending you want to opt for.
e.g. PuTTY
image
Arduino Serial Monitor

If you have no control over the sending side you may need to cater for all scenarios, but the default order for the case of both characters being sent is CR/LF (despite Arduino Serial Monitor mention it as NL & CR but sending it as CR/LF :wink: - NL [new line] isn’t really standard terminology. NL would be the abstract for any of the above mentioned possible line endings - LF [line feed] is the “official name” for the character with the byte value 0x0A/10 (corrected as pointed out by @Muskie :+1: )).

If you “remember” how old fashioned typewriters worked, you had a lever to first push the carriage all the way back to the beginning of the line and once you hit the limit it would ratchet the paper drum one line on. That’s where this convention stems from.

3 Likes

Minor correction:
Line Feed character is 0x0A (10)
Carriage Return character is 0x0D (13)

2 Likes

Typewriters … I’ve heard of such things. :grinning:

So to summarize, on both macOS and Windows (at least running Parallels on macOS), pressing the return key sends \r\n, in that order: that’s why Serial.readStringUntil(’\r’) works on both platforms, whereas if I use ‘\n’, an extra ‘\r’ shows up in the string itself.

Of course, this completely contradicts the stackoverflow.com article about all platforms sending just ‘\r’, unless they have a different enter key in mind, so since it doesn’t fit into my mental model of what’s happening, I will just ignore it (or maybe conclude that the author never worked on macOS).

Thanks all, for you help! This forum is an incredible resource.

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.