Serial communication timing issue

Hello,

I have a project with 2 arduino mega and Nextion touch panel. Connection diagram below.
arduino connection
I need to transfer a message 48-50 characters from Arduino Mega #2 to Arduino Mega #1. Both boards communicate on 115200 baud. I am on communication testing stage. I want to present on the screen one of the messages transfered from Mega #2 to Mega#1. I'm facing with an issue that messages are lossing own boundaries and instead of receving a formatted messages as commented below for Mega#2 I'm receving scrumbled message.
Board #2 generates and sends the messages in batches. Not sure that it's correct way, but I don't know how to stay in buffer size:

//A01011100
  //B0101
  //C110
  //D01010101010101011
  //E11110000  
  message = "C";
  message.concat(counter);  //where counter is 3 digits int value

  Serial1.println("A01011100");
  Serial1.println("B0101");
  Serial1.println(message);
  Serial1.println("D01010101010101011");
  Serial1.println("E11110000");

Mega #1 code below. Code is a bit not organized, 'cuz I on communication testing stage. As I understand I have an issue with buffer size but I can't figure out how to fix it. Any help is appreciated!

I read tutorial here: Serial Input Basics - updated - #3 by Robin2 but stil can't get how to transfer properly big messages.

#include "EasyNextionLibrary.h"
EasyNex myNex(Serial1);

String stringMessage;

const byte numChars = 32;
char receivedChars[numChars];
String statusInsertA = "";
String statusInsertB = "";
String statusInsertC = "";
String statusInsertD = "";
String statusInsertE = "";
boolean newData = false;
int buttonState = 0;
int pageNumber = 0;
int outputLedState = 0;
String messageToReceiver = "";

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial2.begin(115200);
  myNex.begin(115200);
  pageNumber = 0;
  buttonState = 0;
  stringMessage = "";
  delay(500);
}

void loop() {
  pageNumber = myNex.currentPageId;
    
  readDataFromReceiver();
  sendDataToReceiver();
  parseReceivedMessage();
  updateTransferData();

  if(pageNumber == 0){
    buttonState = myNex.readNumber("bt0.val");
    myNex.writeStr("t1.txt", stringMessage);
  } else {
    buttonState = 0;
  }
  outputLedState = (pageNumber || buttonState);
  messageToReceiver = String(outputLedState);
  
  digitalWrite(LED_BUILTIN, pageNumber || buttonState);
}

void parseReceivedMessage()
{
  switch (receivedChars[0]) {
    case 'A':
      statusInsertA = receivedChars;
      break;
    case 'B':
      statusInsertB = receivedChars;
      break;
    case 'C':
      statusInsertC = receivedChars;
      break;
    case 'D':
      statusInsertD = receivedChars;
      break;
    case 'E':
      statusInsertE = receivedChars;
      break;
  }
}

void sendDataToReceiver(){
  Serial2.println(pageNumber || buttonState);
}

void readDataFromReceiver() {
  static byte ndx = 0;
  char endMarker = '\n';
  char rc;

  while (Serial2.available() > 0 && newData == false) {
    rc = Serial2.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 updateTransferData() {
  if (newData == true ) {
    if (statusInsertC != stringMessage ) {
      
      stringMessage = statusInsertC;      
    }    
    newData = false;
  }
}

The very first thing I would do is begin to debug the program by putting in serial.Print() lines to track the logic and display the value being read. As things are right now, you do not know where to look or where to change anything.

Can you show what you are receiving.

You can make a connection from the transmitting Arduino to the Receiving Arduino and let the receiving processor tell the transmitter when to start and stop. Have it stop when the buffer is about 80% full or fuller and start again when it is 30% or less.

As others said u need a send and receive buffers.

For example one function writes string while checking for terminating null to buffer by calling another function which pushes chars to buffer without checking for terminating char.

void Write_String_To_Buffer(const char* const STR_PTR)
{
tByte i = 0;
while (STR_PTR[i] != '\0') {
      Write_Char_To_Buffer(STR_PTR[i]);
      i++;
}}

And then when buffer is ready(its up to you when and under what conditions determine its ready) to be sent function is sending ready to send request to receiving side and waiting for ready to accept response and then is sending. Rs232 is using lsb first method btw.
So u kinda r implementing xon and xoff method. When receiving side cant keep up with processing its sending xon to sender side and if its ok to accept sending xoff. I would increase both senders and receivers buffers maximum possible value. And aso would decrease baudrate to 9600 to be within error rate of 2.5% just in case then slowly increase it to 115 or even higher.

Your readDataFromReceiver will read a completely random number of characters, with no guarantee you read a complete message. Serial data takes TIME to arrive, but you can process characters MUCH faster than they arrive. Problem is, you stop reading characters as soon a available() returns false, which can, and WILL, occur between characters, in the middle of a message. You then, effectively, process whatever fragment of a message you have received, then discard it, even though it was likely NOT a complete message.

You need to either wait, in readDataFromReceiver for an entire message to arrive, or, MUCH better, receive and buffer as many characters as are available, then return, SAVING those characters, and adding to the buffer on subsequent calls to readDataFromReceiver until you have an entire message buffered. Only then do you process the buffered message.

I added into the loop next part of the code:

  Serial.println("A = " + statusInsertA);
  Serial.println("B = " + statusInsertB);
  Serial.println("C = " + statusInsertC);
  Serial.println("D = " + statusInsertD);
  Serial.println("E = " + statusInsertE);

As result I start receiving:
A = A01011100
B = B11100010D0101001
C = C193C193
D = D01
E = E11110000101010101101
A = A01011100C327
B = B0101A0101110
C = C
D = D101
E = E1101

How I can measure the buffer?

It's what I thought. But how I can wait for an entire message to arrive? What do you mean to read as much characters as are available? How can I check how many characters are available for me?
Do you have any examples of code that I try to modify and apply into my scenario?

I'm still thinking is it right way to send the data from transmitter unit using multiple Serial1.println methods. Or I need to combine all parts into a 1 giant message and then send.

Buffer length DOESN'T MATTER, provided you process the message as it comes in, but you need to either buffer it again until it's complete, or process it in chunks as it's received.
Software can read and process the bytes much faster than they are received, if designed properly. However, throwing delay() into the mix almost guarantees you'll overrun your serial buffer sooner or later.

Read this:

Serial Input Basics - updated - Using Arduino / Introductory Tutorials - Arduino Forum

and learn the RIGHT way to handle communications data. Whether youe messages are long or short makes no difference. What you're doing now WILL fail. Do it properly as shown in that link, and you'll never have a problem. And, you won't be wasting tens of billions of CPU cycles doing nothing useful.

solved

So what was it, did you remove/reduce the delay(), or what???

That is part of the software you have to write. There may be some libraries for this but I am not familiar with them. Try this link: SerialTransfer - Arduino Reference I used this search term "Arduino data transfer library" and got a lot of hits. Try it you might find something you like.

I didn't have delays except in start section. But :slight_smile: I did changed slave unit to send the message a bit in different format:

if (millis() >= timerDataTransmit){
    Serial1.print("A01011100");
    Serial1.print("B0101");
    Serial1.print(message);
    Serial1.print("D01010101010101011");
    Serial1.println("E11110000");
    timerDataTransmit = millis() + 250;
  }

I will try to dig a little bit deeper into this question. But so far it works. In the beginning when I had connected only 2 arduino boards the code was working fine, but when I added 3rd component into the circuit, then it star giving me an issues.
As RayLivingston mentioned in his reply, they recevier trying to read whatever is available for him. Message can be present without head or without tail. It actually what was happening, the system was saving available data into char array, unfortunately Serial.available() doesn't tell that message is ready, it just tells that I have stuff in my buffer and if I want I can work with it.

I read couple times again about Arduino- Serial and simple examples from Robin2 Serial Input Basics - updated . Everything looks simple until you are adding more stuff into the systmtem. Then it makes my understanding of Serial to fall apart. I wish it was that easy as TCP/UDP.

No one has commented on this yet, but this will likely cause issues around the time millis() wraps around (after about 49.7 days). The "canonical" and only correct way to run a periodic task using millis() is by subtracting the previous time that a task ran from millis(), like this:

unsigned long dataTransmitPeriod = 250;
// ...
if (timerDataTransmit - millis() >= dataTransmitPeriod){
    Serial1.print("A01011100");
    Serial1.print("B0101");
    Serial1.print(message);
    Serial1.print("D01010101010101011");
    Serial1.println("E11110000");
    timerDataTransmit += dataTransmitPeriod;
  }

(And timerDataTransmit should be unsigned long if it's not already.)

Good thing to know. Thanks!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.