Base story: VB.NET application controls Arduino. From the application I SerialPort.Write() 9 different strings and read 1. Therefore Arduino reads 9 and writes 1. All of them work except for just one. For a week I believed the problem is in my VB sub. After quite some hours into MSDN forums I realized it's the Arduino that can't read the command that fast as in that same sub it has to write as well. Then I found this topic with some very nice ideas and explanations. So probably doing Serial.setTimeout(100) will resolve my issue but after what I read:
HazardsMind:
Not to start anything, just pointing something out for those who read this thread and are just learning about Strings and strings. What you do after looking at the following code is up to you.
//Uncomment (one at a time) to see the difference in memory used
//String text = "Hello World"; // 3,518 total bytes -> 1,648 bytes + base line
//char text[] = "Hello World";// 1,964 total bytes -> 94 bytes + base line
//char * text = "Hello World";// 1,970 total bytes -> 100 bytes + base line
void setup()
{
Serial.begin(115200); //base line 1,870 bytes with next line commented out.
Serial.print(text); // .print('0') -> 1,904 bytes : .print("0") -> 1,954 bytes
}
void loop() { }
... I see things different now. The first part would be to change all of my 10 string commands to integer. So my question - what is the difference between Serial.read() and Serial.readString()?
If my application writes for example integer 1 instead of string "openAuto", will Serial.read() figure out that 1 is integer or it will read it as string "1"? If it reads is as string, can I change that to an integer? And most importantly, will this optimize the whole project in terms of time?
P.S. - I believe the default serial timeout of 1000ms affects both read() and readString() so is there a point?
Serial.read() returns a char* which you can use to make a string. Serial.readString() returns a String (capital makes all the difference ;)) Serial.readString() will block until: a) it has a time out; or b) it receives a newline.
So if you write the char '1' to the serial, Serial.read() returns a char '1'.
Using strings is prefered above Strings although Strings look easier at first glance, because it's pretty easy to make Swiss cheese of your memory with Strings.
Okay, officially an int which contains or the received char or -1 is there was nothing to read. Yeah, pretty stupid
Okay, officially an int which contains or the received char or -1 is there was nothing to read. Yeah, pretty stupid
Somewhat disagree with this “stupid” comment.
Read() should be able to return any byte. So any value from 0 to 255 is a potential valid returned value. How would you inform the caller of a read failure if you don’t want to pass an extra argument to the function and keep it very low impact/easy to code with
=> Adding one transient byte to the returned info serves as this error flag and for C convenience (not possible to return a tuple) both are merged into a signed int.
Where it’s is “stupid” or shortsighted is the chosen type. it would have been better to anticipate larger int representation and force returning a int16_t though to ensure we don’t get 4 bytes back when only 2 are necessary
That's where we have Serial.available() for. For 99% of the uses here it would not even matter what it returned if the data was not valid because we don't check it. Don't need to if we used .available() beforehand. So I would have been happy with it returning the null char.
This is golden! Sorry I couldn't find it pre-posting.
septillion:
Serial.read() returns a char* which you can use to make a string. Serial.readString() returns a String (capital makes all the difference ;)) Serial.readString() will block until: a) it has a time out; or b) it receives a newline.
So if you write the char '1' to the serial, Serial.read() returns a char '1'.
Using strings is prefered above Strings although Strings look easier at first glance, because it's pretty easy to make Swiss cheese of your memory with Strings.
Okay, officially an int which contains or the received char or -1 is there was nothing to read. Yeah, pretty stupid
septillion:
That's where we have Serial.available() for. For 99% of the uses here it would not even matter what it returned if the data was not valid because we don't check it. Don't need to if we used .available() beforehand. So I would have been happy with it returning the null char.
Well that's for the 1% that don't use available() and want to do it all in one go
I agree it's a common sense to use first available() and then read() but on an AVR it's actually just slowing things down a bit (there is a modulo calculation happening) and you get the -1 from read() if nothing is available (it does not reflect on an error at hardware level when trying to read because the byte is already in the buffer). So could argue that available() is useless and peek() or read() could serve that purpose.
From a semantic point of view, read() should definitely not return a char as this is also used to read bytes (binary data) and char could be signed - and I also disagree that read should fail silently.. in modern world could throw an exception but that leads to complex constructs...
anyway that's the way it is and likely won't change, so moot point
septillion:
That's where we have Serial.available() for. For 99% of the uses here it would not even matter what it returned if the data was not valid because we don't check it. Don't need to if we used .available() beforehand. So I would have been happy with it returning the null char.
Serial.read() is analogous to functions like getchar(), istream.get(), etc. They all return some value that cannot occur in the input stream to indicate "end of file" or a read error. It's pretty idiomatic in C and C++ to use them as the condition in an if/while/for statement:
int c;
while ((c = getchar()) != EOF) {
// process 'c'
}
int c;
while ((c = std::cin.get()) != EOF) {
// process 'c'
}
int c;
while ((c = Serial.read()) != -1) {
// process 'c'
}
J-M-L:
Where it’s is “stupid” or shortsighted is the chosen type. it would have been better to anticipate larger int representation and force returning a int16_t though to ensure we don’t get 4 bytes back when only 2 are necessary
I don't think it's stupid. On systems where int is 32 bits wide, the overhead of handling a 4-byte value is usually negligible or non-existent. Those systems typically have 32-bit registers, so it's no big deal to test a 32-bit value vs a 16-bit value (functions return values in registers on most architectures).
J-M-L:
Sure but they would also have 16 bit registers
I haven't seen any 32-bit systems that have separate 16-bit and 32-bit registers, or any that are significantly (or at all) slower at checking a 32-bit value than a 16-bit value. And if you're worried about a cycle or two out of hundreds or thousands, you probably shouldn't use C++ in the first place.
(Also, it's probably quicker on virtually any system, whether it's 32-bit or not, to see if a value is less than 0 rather than if it equals -1.)
In any case, calling Serial.available() before Serial.read() has significantly higher overhead than any difference between a 16-bit or 32-bit return value type.
fair enough - agree it's far fetched for small win - just intellectual satisfaction of using only 2 bytes, when 2 are needed (actually 1 bytes and 1 bit could do) and letting the compiler decides what’s best for the architecture giving all the info we have.
That being said, if I remember correctly the Tensilica’s L106 Diamond series (which you find in ESP) offers eight dedicated 16-bit operand registers. This is not uncommon in RISC architecture to allow for 16-bit parallel DMA for high speed operations.
agree indeed that calling available() has a significant cost that is often underestimated.
I usually code examples with using available() because I found this helps structure the code reading for beginners.
"if something is available then read it and do something with it" is apparently easier to understand than "if read does not fails then do something with the data"...
available() has it's use if you want to wait (for whatever reason) to know you have more than 1 byte in the stream buffer.