I know I'm not the first nor the last to not fully grasp how to handle the serial part.
I have a sim800l V2 module that I'm trying to incorporate into a project so I've read a lot about how serial works (including this article and this amazing piece of work). I do get the concept of serial reading handles a single character at the time.
As far as I understand the arduino loop often will be a lot faster than any serial, so using the while(mySerial.available()) command will result in an empty buffer and proceed with whatever other stuff that's in the loop.
But why does this work then (found in the wild, but I cannot backtrack to where):
void loop()
{
while (sim800.available()){
parseData(sim800.readString());//Calls the parseData function to parse SMS
}
while (Serial.available()){
sim800.println(Serial.readString());
}
}
void parseData(String buff) {
Serial.println(buff);
unsigned int index;
//Remove sent "AT Command" from the response string.
index = buff.indexOf("\r");
buff.remove(0, index + 2);
buff.trim();
if (buff != "OK") {
index = buff.indexOf(":");
String cmd = buff.substring(0, index);
cmd.trim();
buff.remove(0, index + 2);
Serial.println(buff);
//Parse necessary message from SIM800L Serial buffer string
if (cmd == "+CMT") {
//get newly arrived memory location and store it in temp
index = buff.lastIndexOf(0x0D);//Looking for position of CR(Carriage Return)
msg = buff.substring(index + 2, buff.length());//Writes message to variable "msg"
msg.toLowerCase();//Whole message gets changed to lower case
index = buff.indexOf(0x22);//Looking for position of first double quotes-> "
PHONE = buff.substring(index + 1, index + 12); //Writes phone number to variable "PHONE"
}
}
}
Or perhaps more correctly; why does it seem to work but in reality it doesn't work too well if I include a function to write the data to a SD card. And what would be the correct approach when I cannot control the incoming SMS in terms of starting and ending character?
At a glance, it looks like both while statements should be if statements.
If there is incoming, get it, parse it and do whatever.
If there is outgoing, read it in its entirety and send it out.
You could try that, I cannot. The general rule, however, is that the loop() function should handle, um, looping, meaning any loops in that function should be scrutinized and judged appropriate or changed. To not loop.
If I use if statements instead of while statements I guess I would also need to read the single character into a buffer and handle it once the entire message has been read, correct?
If I don't do that parseData will just execute on a single character every time - or am I missing something?
Correct. Or at least, if your code is well written and non-blocking it is correct.
I can't properly comment on the code you present because I can't fully understand it, and in any case, why would I comment on code you know little about but say:
?
So, you've read the serial input basics tutorial, what is it about that tutorial that you would like explaining?
Some limited comments on the code you presented:
Data, as you suggest, comes in a lot slower than loop (should) loops, so when 'available' is true it means there is 1 or more characters available in the serial receive buffer, it does not mean the whole message is there, that is for you to do. You can empty the buffer pretty quickly and look at the data, save it in your own buffer and see if it represents a complete message and take action accordingly. Maybe you find 2 characters, but your message is 10 characters, so you know it's incomplete, you save the 2, exit the while loop, then next time round check for more characters, keep doing this until you have all 10, then you can do something with the data.
More than that I can't tell you because nothing I can type here will be half as good at the tutorial, all I can do is clarify specific points from the tutorial if you ask questions about them.
No. The readString() method on both objects does what it says, it sits there and reads a string.
Until it times out, a period you can adjust.
You might want to use readStringUntil(), which allows you to specify the character that signals the end of the String.
Or… you might yes, embrace fully the concept of gathering them and buffering them yourself, and enjoy doing that (!) as well as benefiting now or in the future from having let your loop() function run freely.
Pretty sure the article you linked goes into the details. Not hard at least conceptually. Give it a shot.
I wonder why you bother replying when you really don't want to help anyway. Thankfully there's other forum members who are more than willing to help out even if it's a trivial question for the more advanced users...
I have this non-blocking "get line" function that might be instructive.
It reads characters if they are available, and assembles them into a new buffer, and returns non-zero if there was a "terminating character." Otherwise it returns 0 immediately.
/*
* getLine
* Read a line of text from Serial into the internal line buffer.
* With echoing and editing!
* Non-blocking. Returns 0 until end-of-line seen.
*/
uint8_t parserCore::getLine ()
{
int c;
c = S->read(); // Note: S is a local/private "Stream" object...
switch (c) {
case 127:
case CTRL('H'):
/*
Destructive backspace: remove last character
*/
if (inptr > 0) {
S->print("\010 \010");
buffer[--inptr] = 0;
}
break;
case CTRL('R'):
/*
Ctrl-R retypes the line
*/
S->print("\r\n");
S->print(buffer);
break;
case CTRL('U'):
/*
Ctrl-U deletes the entire line and starts over.
*/
S->println("XXX");
reset();
break;
case CTRL('J'):
case CTRL('M'):
buffer[inptr++] = '\n';
S->println(); /* Echo newline too. */
return inptr;
case -1:
/*
No character present; don't do anything.
*/
return 0;
default:
/*
Otherwise, echo the character and put it into the buffer
*/
buffer[inptr++] = c;
S->write(c);
}
return 0;
}