Serial read/print and delay problem

Hello, im using the example by Serial Input Basics - updated - Introductory Tutorials - Arduino Forum to read a NMEA strings WIMDA and GPRMC from GPS unit, it send me the 2 strings every 1 second.

////////////////////////////////////////////////////////////
#include <SoftwareSerial.h>

SoftwareSerial airmar(10,11);
// Example 2 - Receive with an end-marker

const byte numChars = 72;
char receivedChars[numChars]; // an array to store the received data

boolean newData = false;

void setup() {
Serial.begin(19200);
airmar.begin(4800);
Serial.println("");
}

void loop() {
recvWithEndMarker();
showNewData();
}

void recvWithEndMarker() {
static byte ndx = 0;
char endMarker = '\n';
char rc;
airmar.listen();

while (airmar.available() > 0 && newData == false) {
rc = airmar.read();

if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
ndx = 0;
newData = true;
}
}
}

void showNewData() {
if (newData == true) {
Serial.print("Dato_leido");
Serial.println(receivedChars);
newData = false;
}

}

And in the Serial terminal

Dato_leido$WIMDA,29.9818,I,1.0153,B,23.7,C,,,,,,,323.1,T,311.6,M,0.2,N,0.1,M2C
Dato_leido$GPRMC,002411.20,A,3151.7113,N,11640.1045,W,0.0,268.4,270517,11.5,E,D
1
Dato_leido$WIMDA,29.9877,I,1.0155,B,23.7,C,,,,,,,323.2,T,311.7,M,0.3,N,0.2,M23
Dato_leido$GPRMC,002412.20,A,3151.7113,N,11640.1045,W,0.0,291.2,270517,11.5,E,D
1
Dato_leido$WIMDA,29.9788,I,1.0152,B,23.7,C,,,,,,,323.1,T,311.6,M,0.2,N,0.1,M2B
Dato_leido$GPRMC,002413.20,A,3151.7113,N,11640.1045,W,0.0,259.8,270517,11.5,E,D
1
Dato_leido$WIMDA,29.9847,I,1.0154,B,23.7,C,,,,,,,6.0,T,354.5,M,0.3,N,0.2,M*24

So it's ok reading the data from GPS unit

//////////////////////////////////

but if i put a delay (100, 1000, 2000, etc....)

void loop() {
recvWithEndMarker();
showNewData();
delay(200);
}

at the begin or end of loop, the serial terminal send:

Dato_leido$GPRMC,002624.20,A,3151.7118,N,11640.1043,W,0.0,170.7,270517,11$GPRMC,0
Dato_leido$WIMDA,29.981$GPRMC,002631.20,A,3151.7118,N,11640.1044,W,0.0,16$GPRMC,0
Dato_leido$GPRMC,002645.20,A,3151.7118,N,11640.1044,W,0.0,33.1,270517,1$G$GPRMC,0
Dato_leido$GPRM$GPRMC,002654.20,A,3151.7117,N,11640.1045,W,0.0,332.8,2705$GPRMC,0
Dato_leido$WIMDA,29.9818,I,1.0$GPRMC,002701.20,A,3151.7117,N,11640.1045,W$GPRMC,0
Dato_leido$GPRMC,002715.20,A,3151.7116,N,11640.1045,W,0.1,213$GPRMC,00271$GPRMC,0
Dato_leido$WIMDA,29.9759,I,1.0151,B,23.6,C,,,,,,,54.3,T,42.8,M,0.2,N,0.1,$GPRMC,0
Dato_leido$WIMDA,29.9788,I,1.0152$GPRMC,002731.20,A,3151.7116,N,11640.104$GPRMC,0

So the NMEA strings are mixed one in another.

Whats is my error?
Isn't suppose that while serial.available are for to read any NMEA string until \n char and not mix with the other?
how i can resolve that?

Thank you so much.

Whats is my error?

Isn't it obvious? What did you change between the working code and the non-working code? You added a useless delay() call. Why?

Isn't suppose that while serial.available are for to read any NMEA string until \n char and not mix with the other?

What is going to happen when the buffer overflows because you aren't reading often enough?

how i can resolve that?

I think that that is so obvious you can figure it out yourself.

At 19200 you receive roughly 2 bytes per millisecond. The Serial implementation (both hardware and software serial) have a software buffer that is filled with received data. This buffer however has a limited size (64 bytes); once it's full other data is discarded.

In 200ms delay, you can (worst case) receive 2*200 equals 400 bytes; 400-64 equals 336 missed data bytes if the GPS constantly sends data. You can adjust the calculation for 2 strings per second.

Note 1:
recvWithEndMarker() uses a non-blocking approach. This means that it might have to be called several times before you have a complete message. If you don't use delay(), loop() is called thousands of times per second so there is no issue and the code can keep up with the data.
Note 2:
You can make the code a little more solid if you use recvWithStartEndMarkers (start marker will be '$' if I look at your output). Note that this will not solve your problem of missed data.

// EDIT
Please use code tags when posting code; please edit your post and add
** **[code]** **
before your code and
** **[/code]** **
after your code

PaulS:
Isn't it obvious? What did you change between the working code and the non-working code? You added a useless delay() call. Why?
What is going to happen when the buffer overflows because you aren't reading often enough?
I think that that is so obvious you can figure it out yourself.

I wish to see data every second, so i add a delay(1000)

I didn't know that the byffer was overflow because is not reading enough (and mix the 2 strings), i was thinking the while (airmar.available() > 0 && newData == false and the endmarker \n was reading all the NMEA string.

sterretje:
At 19200 you receive roughly 2 bytes per millisecond. The Serial implementation (both hardware and software serial) have a software buffer that is filled with received data. This buffer however has a limited size (64 bytes); once it's full other data is discarded.

In 200ms delay, you can (worst case) receive 2*200 equals 400 bytes; 400-64 equals 336 missed data bytes if the GPS constantly sends data. You can adjust the calculation for 2 strings per second.

Note 1:
recvWithEndMarker() uses a non-blocking approach. This means that it might have to be called several times before you have a complete message. If you don't use delay(), loop() is called thousands of times per second so there is no issue and the code can keep up with the data.
Note 2:
You can make the code a little more solid if you use recvWithStartEndMarkers (start marker will be '$' if I look at your output). Note that this will not solve your problem of missed data.

// EDIT
Please use code tags when posting code; please edit your post and add
** **[code]** **
before your code and
** **[/code]** **
after your code

Thank you, I didn't that i need to call recvWithEndMarker() ) (or other functions) several times to read one NMEA string, i was thinking the while Serial.available>0 was enough only to use for one time for read all the string using a endmarker.

If the gps sends e.g. 72 bytes, it is very well possible that after you have read e.g. the first byte in the receive function, the transmission of the 2nd byte is still on progress and hence available() will return zero at that moment. Only the second or third or Nth call to the receive function after that will give you a new byte (assuming no delay).

If the gps sends the two messages every second (as you stated in the opening post), there is no reason for delay. Just display when you get them.

CLiera:
I wish to see data every second, so i add a delay(1000)

That will have almost the opposite effect - just screwing everything up. If you want to view something at 1 second intervals you have to allow the something to work during the second between views - by using delay() you block everything from working.

Have a look at how millis() is used to manage timing without blocking in Several things at a time

...R

it send me the 2 strings every 1 second.

I wish to see data every second

Since you only print the data when it is complete, you AUTOMATICALLY will see data once per second. The delay() was completely useless.

Robin2:
That will have almost the opposite effect - just screwing everything up. If you want to view something at 1 second intervals you have to allow the something to work during the second between views - by using delay() you block everything from working.

Have a look at how millis() is used to manage timing without blocking in Several things at a time

...R

Hello Robin, i see at your post that if i use the program recvWithStartEndMarker() all characters are discarded until the start-marker is detected.

Im am use it, but if i need to display data after e.g. 10 seconds (using a delay) the output data still NMEA strings are mixed one in another.

I dont know why the loop() it need to be called several times to read a complete NMEA string, i was thinking that the while serial.available and recvInProgress can wait to read one NMEA string and after some time use that string at the end of the loop()

Let's say that loop runs 10,000 times a second; that is 100 us per iteration. At 19200 baud, it takes about 500 us for a byte to be transferred.

So loop() calls recvWithStartEndMarker() and there is no data available and recvWithStartEndMarker() returns to loop(). And loop() calls it again and again and again and ..... and at time T a byte is available, the while statement evaluates to true and the code reads the byte. That takes maybe 1 us.

In that 1 us that it takes to read the byte, no new data has arrived as it takes 500 us for the next byte to be transferred; so the while now evaluates to false and recvWithStartEndMarker() returns to loop().

The code now starts all over again with (1) till the next byte arrives (at T=500). And so on and so on.

The reason for this approach is that the code does not block and in those 500 us your code can do other useful things like fading a led, reading buttons and so on.

I hope that makes it a little clearer.

One way to achieve what you want is to use a two-dimensional array that can hold two messages

/*
  variables for serial receive
*/
const byte numChars = 72;
char receivedChars[numChars];   // an array to store the received data
boolean newData = false;

/*
  message buffer to hold two consequtive messages
*/
char messages[2][numChars];

void setup()
{
  ...
  ...
}

void loop()
{
  recvWithStartEndMarker();
  processNewData();
  bwod();
}

processNewData will be responsible for copying a received message into the messages buffer.
bwod is a simple blink-without-delay to show how you can do something every 10 seconds.

The below are the functions that relate to the serial receive, processing and displaying

/*
  receive data
*/
void recvWithStartEndMarker()
{
  ...
  ...
}

/*
  copy received messages to messages array
*/
void processNewData()
{
  // a counter to count the number of received messages
  static byte messageCounter = 0;

  // if new message has arrived
  if (newData == true)
  {
    // clear the newData flag as the received message is now being processed
    newData = false;

    // for debugging, display the received message
    Serial.print("DEBUG: "); Serial.println(receivedChars);

    // copy the received message to the current location in the messages buffer
    strcpy(messages[messageCounter], receivedChars);
    // increment the counter so the next time we received a message, it will be stored in the next location in messages
    messageCounter++;
    // if counter indicates a position outside messages, reset
    if (messageCounter == 2)
    {
      messageCounter = 0;

      // for debugging, display both messages here
      // remove this later
      showMessages();
    }
  }
}

/*
  show stored messages
*/
void showMessages()
{
  for (int msgCnt = 0; msgCnt < 2; msgCnt++)
  {
    Serial.print("Message "); Serial.println(msgCnt + 1);
    Serial.print(": "); Serial.println(messages[msgCnt]);
  }
}

and bwod

/*
  blink without delay; 10 second interval
*/
void bwod()
{
  static unsigned long lastTime = 0;
  if (millis() - lastTime >= 10000)
  {
    lastTime = millis();
    digitalWrite(13, !digitalRead(13));
  }
}