Weird Serial.read issue

Hi all, I have a strange (probably timing related) issue on a Serial logger.

If I listen to a single serial port everything is fine and no data at all is skipped.

Using this code:

const byte numChars = 32;
char receivedChars[numChars];

boolean newData = false;

void setup() {
    Serial.begin(9600);
    Serial.println("<Arduino is ready>");
}

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

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;
 
 // if (Serial.available() > 0) {
    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();

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

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

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

However, If I modify the code above in order to listed to 3 different hardware serial port, values mixes up between themselves randomly.

Code:

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

const byte numChars1 = 8;
char receivedChars1[numChars1];
const byte numChars2 = 8;
char receivedChars2[numChars2];
const byte numChars3 = 8;
char receivedChars3[numChars3];

boolean newData1 = false;
boolean newData2 = false;
boolean newData3 = false;

unsigned long previousMillis = 0;
const long interval = 1000;

void setup() {
  Serial.begin(9600);
  Serial1.begin(9600);
  Serial2.begin(9600);
  Serial3.begin(1200);
  lcd.begin(16, 2);
  lcd.setCursor(0, 1);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("P: ");
  lcd.setCursor(8, 0);
  lcd.print("F: ");
  lcd.setCursor(0, 1);
  lcd.print("T: ");
}

void loop() {
  recvWithStartEndMarkers1();
  recvWithStartEndMarkers2();
  recvWithEndMarker3();
  output();
  delay(150);
}

void recvWithStartEndMarkers1() {
  static boolean recvInProgress1 = false;
  static byte ndx1 = 0;
  char startMarker1 = 'a';
  char endMarker1 = 'b';
  char rc1;

  while (Serial1.available() > 0 && newData1 == false) {
    rc1 = Serial1.read();

    if (recvInProgress1 == true) {
      if (rc1 != endMarker1) {
        receivedChars1[ndx1] = rc1;
        ndx1++;
        if (ndx1 >= numChars1) {
          ndx1 = numChars1 - 1;
        }
      }
      else {
        receivedChars1[ndx1] = '\0';
        recvInProgress1 = false;
        ndx1 = 0;
        newData1 = true;
      }
    }

    else if (rc1 == startMarker1) {
      recvInProgress1 = true;
    }
  }
}



void recvWithStartEndMarkers2() {
  static boolean recvInProgress2 = false;
  static byte ndx2 = 0;
  char startMarker2 = '<';
  char endMarker2 = '>';
  char rc2;

  while (Serial2.available() > 0 && newData2 == false) {
    rc2 = Serial2.read();

    if (recvInProgress2 == true) {
      if (rc2 != endMarker2) {
        receivedChars2[ndx2] = rc2;
        ndx2++;
        if (ndx2 >= numChars2) {
          ndx2 = numChars2 - 1;
        }
      }
      else {
        receivedChars2[ndx2] = '\0';
        recvInProgress2 = false;
        ndx2 = 0;
        newData2 = true;
      }
    }

    else if (rc2 == startMarker2) {
      recvInProgress2 = true;
    }
  }
}

void recvWithEndMarker3() {
  static byte ndx3 = 0;
  char endMarker3 = '\n';
  char rc3;

  while (Serial3.available() > 0 && newData3 == false) {
    rc3 = Serial3.read();

    if (rc3 != endMarker3) {
      receivedChars3[ndx3] = rc3;
      ndx3++;
      if (ndx3 >= numChars3) {
        ndx3 = numChars3 - 1;
      }
    }
    else {
      receivedChars3[ndx3] = '\0';
      ndx3 = 0;
      newData3 = true;
    }
  }
}

void shownewData1() {
  if (newData1 == true) {
    Serial.print("P:");
    Serial.print(receivedChars1);
    Serial.print(" Bar");
    lcd.setCursor(4, 0);
    lcd.print(receivedChars1);
    newData1 = false;
  }
  else {
    Serial.print("P:");
    Serial.print("*");
    lcd.setCursor(4, 0);
    lcd.print("*");
  }
}

void shownewData2() {
  if (newData2 == true) {
    Serial.print(" |F:");
    Serial.print(receivedChars2);
    Serial.print(" l/min");
    lcd.setCursor(11, 0);
    lcd.print(receivedChars2);
    newData2 = false;
  }
  else {
    Serial.print(" |F:");
    Serial.print("*");
    lcd.setCursor(11, 0);
    lcd.print("*");
  }
}

void shownewData3() {
  if (newData3 == true) {
    Serial.print(" |T:");
    Serial.print(receivedChars3);
    Serial.println(" kPa");
    lcd.setCursor(4, 1);
    lcd.print(receivedChars3);
    newData3 = false;
  }
  else {
    Serial.print(" |T: ");
    Serial.println("*");
    lcd.setCursor(4, 1);
    lcd.print("*");
  }
}

void output() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    shownewData1();
    shownewData2();
    shownewData3();
  }
}

And this is the output I get:

P:* |F:+0.0 l/min |T: *
P:* |F:+0.0 l/min |T: *
P:* |F:+0.0 l/min |T: *
P:* |F:+0.0 l/min |T: *
P:* |F:+0 l/min |T: *
P:* |F:+0.0<+0 l/min |T: *
P:* |F:<+0.0 l/min |T: *
P:* |F:+0.0 l/min |T: *
P:* |F:+0.0 l/min |T: *
P:* |F:+0 l/min |T: *
P:* |F:+0.0 l/min |T: *
P:* |F:+0.0 l/min |T: *
P:* |F:+0.0 l/min |T: *

As you can see, there are missing values (sometimes even the start or end marker is printed).

What am I doing wrong?

I assume that this is a Mega. What are the devices sending the serial data, how are they connected to it and what signal levels are being used ?

This is in the first code

const byte numChars = 32;

yet, you have this in the second code

const byte numChars1 = 8;

could this be an issue

UKHeliBob: I assume that this is a Mega. What are the devices sending the serial data, how are they connected to it and what signal levels are being used ?

The devices are 3 Arduino, 9600 baud, print value in line (not cr or nl) every 50ms, but I can of course change this.

Ps991: This is in the first code

const byte numChars = 32;

yet, you have this in the second code

const byte numChars1 = 8;

could this be an issue

Unfortunately no. But maybe it's related to the buffer not working correctly.

The devices are 3 Arduino

Arduino what?
Serial1, 2, 3 cannot be used on an Uno, so what devices are you using and what is their setup in relation to each other. Maybe even take a good picture or post a schematic for clarification.

Ps991: Arduino what? Serial1, 2, 3 cannot be used on an Uno, so what devices are you using and what is their setup in relation to each other. Maybe even take a good picture or post a schematic for clarification.

Sorry, I'll explain better:

The "log" device, which is the one I'm running the first post code (serial1, 2 and 3) is a Mega.

The "devices" (3 arduinos) are 3 UNO, each one printing on his own serial (serial.print at 9600 br).

Weird thing is that if I "clean" the code removing serial 2 and serial 3, the issue is still there. I must have changed something between the 2 codes but I can't find any difference except for the "1" added at the end of every value (could this be the issue?)

I keep looking, but I can't find anything wrong, and I cant test it unfortunately.

My only guess is that you are sending data too fast, maybe? and you are overflowing the internal buffer?

We should really see the other arduino's code as well, because I am curious how you are sending the data and what data you are sending...

What happens if you remove the "delay(150);" in loop() and change interval to like, 250?

Ps991:
I keep looking, but I can’t find anything wrong, and I cant test it unfortunately.

My only guess is that you are sending data too fast, maybe? and you are overflowing the internal buffer?

We should really see the other arduino’s code as well, because I am curious how you are sending the data and what data you are sending…

What happens if you remove the “delay(150);” in loop() and change interval to like, 250?

That’s the sender code:

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  int value = analogRead(A0);
  float flow = fmap(value, 197, 1023, -6.0, 6.0);
  Serial.print('<');
  if (flow >= 0.00) {
    Serial.print('+');
  }
  Serial.print(flow);
  Serial.print('>');
  delay(50);
}

float fmap(float x, float in_min, float in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

However, I don’t think it’s related to the sender, because with the original receiver code it works like a charm.

UPDATE: If I remove delay, it works a lot better (does it make sense?)

a few things

in the new code you have Serial3 on the wrong baud rate

  Serial3.begin(1200); // shouldn't that be 9600??

then when you poll the 3 serial lines,

  recvWithStartEndMarkers1();
  recvWithStartEndMarkers2();
  recvWithEndMarker3();

and wait for different start/stop chars (a,b or <,> or 0 and \n)

but when I look at the sender code you only seem to send <,>… so Serial1 and Serial3 (assuming it was set to 9600) would never see the a,b or 0,\n (or did you modify that for each sender? which is not needed)

then you do the 3 calls in sequence with a blocking wait for some characters but there is no timeout implementation. So you are never guaranteed to receive a full string. if by bad luck Serial1.available() returns nothing available, you end the while loop but you might still be actually waiting for more data (you should check your boolean recvInProgress1 to see if you need to wait for more.

from a code perspective

  • you don’t need to duplicate the recvWithEndMarker function. just add a parameter which is the Serial port you want to listen to and just use < and > as delimiters in the three uno.

  • implement a timeout and do not return if recvInProgress is true unless you timeout, even if the Serial.available() tells you nothing is available

last but not least, your UNOs are pushing data every 50ms to the serial line - sending a full float + 2 chars - so probably 10 chars which will take 10 ms. as you have 3 calls and print to main serial also slowly what you get from the UNO in the MEGA side, you come back to polling a given serial only every 60ms at best but arduino UNOs are sending data every 50ms, so next time you come check the buffer you don’t fully empty it (you stop at the > sign) and very quickly you will fill up the MEGA serial buffers and run into errors

there are many ways to solve this - the ideal one would be to not block on one Serial and poll them all at the same time building input strings as they come in and scanning them

a short test for you though would be to fix the Serial3 baud rate, have the right end chars being sent by the UNO and in the UNO code, instead of sending data every 50ms, send it ever 200ms - and see if that improves the situation

c0rsa1r: As you can see, there are missing values (sometimes even the start or end marker is printed).

I must be dim this morning because I can't see that.

Post an example of what the output should be.

I don't understand the purpose of these ELSE clauses

  else {
    Serial.print("P:");
    Serial.print("*");
    lcd.setCursor(4, 0);
    lcd.print("*");
  }

Get rid of the delay(150) in loop(). You have sufficient timing control in your output() function.

...R

c0rsa1r: Hi all, I have a strange (probably timing related) issue on a Serial logger.

If I listen to a single serial port everything is fine and no data at all is skipped.

[ BTW using while is always suspect in code like this - replace while with if and then every other 'task' will get a look-in naturally ]

Your code would probably work fine if you didn't try to print out the results at 9600 baud, that's whats slowing everything down and losing information - you try to print out on Serial more characters than are received on Serial, Serial1, Serial2 combined, so your code will backup till data is lost if even one of those is pushing out data at full rate.

I suggest you read from Serial1/2/3 at 9600 and report to Serial at 115200 baud (why use anything less BTW?)