Mega is getting a string from serial port which is terminated as a line or terminated with carriage return and line feed. There is no trailing 0x00. I can do this my self but why bother when it is already done in C function(probably).
The serial input basics tutorial shows how to receive serial data.
It may be "done" in strcpy() but it needs to see one to know when to stop copying the input string.
Read in the chars, char by char, adding them to your input string save the location of the \c and end the string at the \n, pop a \0 where the \c was.
-jim lee
jimLee:
It may be "done" in strcpy() but it needs to see one to know when to stop copying the input string.Read in the chars, char by char, adding them to your input string save the location of the \c and end the string at the \n, pop a \0 where the \c was.
-jim lee
This may well be the best way. I was remembering Visual basic which could easily handle strings which ended CR/LF.
The tutorial that I linked shows how to do what JimLee describes.
Most serial protocols define some sort of delimiter. CR LF STX ETX EOM ... etc.
Very early in your RX code, you save incoming chars to your char[] buffer, and if the delimiter appears in the data, replace it with NUL at the end of your buffer, then it’s a ‘standard’ null terminated c-string.
Even the HEX data format uses line ends as a delimiter, despite also being fixed length records... you can use either. In that case, you can’t use NULL within the data because ‘00’ is a legitimate payload value.
if (Serial.available()) {
char buff[32];
int l = Serial.readBytesUntil('\n', buff, sizeof(buff));
if (l > 0 && buff[l - 1] == '\r') {
l--; // to remove \r if it is there
}
buff[l] = 0; // terminate the string
Serial.println(buff);
}
@juraj, nice contribution, but you might want to declare buff[] globally, or make it static ?!
lastchancename:
@juraj, nice contribution, but you might want to declare buff[] globally, or make it static ?!
why? it is only a temporary read buffer. the 'processing' of the string is inside the if. here it only prints. in real use I would parse it and extract, process and store the essential information right here or a call a function from here.
the string from serial monitor is send as a whole line at once so the readBytesUntil will read it all at once. readBytesUntil waits for next byte until the timeout (default timeout is 1 second and it can be set much smaller with Serial.setTimeout. a good small timeout value depends on baud rate)
Thank you all. If I may say, processing strings is popular because many sensors send strings, and probably is getting even more popular, so these kinds of functions are important. Mine is a barcode reader.
Regards
Leif M
@juraj... You’re assuming the whole string will ‘arrive’ in one cycle.
In my experience that’s risky, because if there’s a single character gap in the timing, your buff[] will be lost.
It’ll work for maybe 90% of messages, but if there’s say... a prefix, some ‘thinking’, followed by a value - things can go wrong...
e,g. a simple example...
Serial.print(“RPM= “);
rpm = calculateRPM(); // <— if this takes longer than one character period...
Serial.printing(rpm);
lastchancename:
@juraj... You’re assuming the whole string will ‘arrive’ in one cycle.
In my experience that’s risky, because if there’s a single character gap in the timing, your buff[] will be lost.
It’ll work for maybe 90% of messages, but if there’s say... a prefix, some ‘thinking’, followed by a value - things can go wrong...
did you read the second part of my comment? readStringUntil waits for the next byte if there is a gap. read the documentation of readStringUntil and try it out.
Sorry, I don’t use / not familiar with the canned string functions... simply because many of them are ‘blocking’...
You’re right, but I’m old-fashioned - used to old style real-time code for truly a sync communications.
Your suggestion is fine for typical Arduino hobby applications.
lastchancename:
Sorry, I don’t use / not familiar with the canned string functions... simply because many of them are ‘blocking’...
You’re right, but I’m old-fashioned - used to old style real-time code for truly a sync communications.
Your suggestion is fine for typical Arduino hobby applications.
it is not really blocking if it is used right. it depends on the sketch. you can explore my projects:
do they look like hobby level?
Perhaps I missed the point, but when
Mega is getting a string from serial port
This non-blocking code automatically adds the '\0' terminator when reading the line of input.
Use input.trim() to remove the \r\n before processing
// readStringUntil_nonBlocking.ino
// Reads chars into a String until newline '\n'
// Pros: Simple. Non-Blocking
// Cons: Does not return until the until_c char is found. i.e. no timeout or length limit
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("readStringUntil_nonBlocking.ino"));
input.reserve(80); // expected line size
}
// read Serial until until_c char found, returns true when found 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) {
while (Serial.available()) {
char c = Serial.read();
input += c;
if (c == until_c) {
return true;
}
}
return false;
}
void loop() {
if (readStringUntil(input, '\n')) { // read until find newline
Serial.print(F(" got a line of input '")); Serial.print(input); Serial.println("'");
input = ""; // clear after processing for next line
}
}
My SafeString library (from Arduino library manager) (detailed tutorial here) works on char[] if you prefer those to Arduino Strings.
SafeString library includes a SafeStringReader that wraps all this up in one statement and adds more features like non-blocking timeouts, input echo, handling of unexpected long lines and flushing of initial partial lines.
#include "SafeStringReader.h"
createSafeStringReader(sfReader, 20, "\r\n"); // a reader for upto 20 chars to read lines terminated by \r or \n or nothing
void setup() {
Serial.begin(9600);
for (int i = 10; i > 0; i--) {
Serial.print(' '); Serial.print(i);
delay(500);
}
Serial.println();
Serial.println(F("SafeStringReader_timeoutFlushing.ino"));
//SafeString::setOutput(Serial);
sfReader.setTimeout(1000); // set 1 sec timeout
sfReader.skipToDelimiter(); // flush initial data until either find delimiter or timeout
sfReader.connect(Serial); // read from Serial
}
void loop() {
if (sfReader.read()) { // got a line or timed out delimiter is NOT returned
if (sfReader.hasError()) { // input length exceeded
Serial.println(F(" sfReader hasError. Input overflowed and skipped to next delimiter."));
}
if (sfReader.getDelimiter() == -1) { // no delimiter so timed out
Serial.println(F(" timeout without newline or carrage return"));
}
Serial.print(F(" got a line of input '")); Serial.print(sfReader); Serial.println("'");
// no need to clear sfReader as read() does that
}
}
Finally if you don't mind blocking your loop you can use Serial.readStringUntil('\n');
Not recommeded, but...
// SerialReadStringUntil.ino
// Reads multiple chars from Serial until find newline '\n' or input times out, i.e. no input for 1sec.
// Pros: Simple. Robust in that it will return the input even if the newline '\n' is missing.
// Cons: Blocking. Each call to readString() can block the loop for a 1sec. Not recommended except for trivial sketches that only do one thing.
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("SerialReadStringUntil.ino"));
}
void loop() {
input = Serial.readStringUntil('\n');
if (input.length() > 0) { // got a word handle it
Serial.print(F(" got a some input '")); Serial.print(input); Serial.println("'");
} else {
Serial.println(F("No input"));
}
}
One note. There is a Serial.readString() function, but its documentation is a bit limited. I think there must be more "The function terminates if it times out (see setTimeout())." if it really receives a string.
I'll write or about this on other subforum.
The difference between readString() and readStringUntil('\n') is that if you send your String terminated by newline ('\n')
readStringUntil('\n') will return as soon as the newline is read
On the other hand readString() always waits for the timeout. That is no more chars received for 1sec
Both of these block waiting for input to arrive and so if no data available both will block your loop for the timeout (1sec default)
I see. Misleading names in my opinion.
And toInt() that actually returns a long
drmpf:
The difference between readString() and readStringUntil('\n') is that if you send your String terminated by newline ('\n')
readStringUntil('\n') will return as soon as the newline is read
On the other hand readString() always waits for the timeout. That is no more chars received for 1secBoth of these block waiting for input to arrive and so if no data available both will block your loop for the timeout (1sec default)
- don't use String
- check available() before starting readBytesUntil to ensure that a batch of characters is incoming. then there is no timeout on first character.
- use readBytesUntil only to read input which is a batch of characters terminated with a specific character
- the best terminating character is a line end character
- Serial Monitor sends the whole line at once when you hit enter
If the sender sends a string (batch of characters) at once they still arrive witch gaps between characters because the Serial communication is very slow compared to the speed of the CPU. That is why readBytesUntil is useful. it waits those milliseconds while the next character arrives.