Mega Serial to Mega Serial

Dear all,

Allow me to pick your fine brains (yet again) :slight_smile:

Although there is plenty of info on Serial comms between 2 Arduinos, it seems there is limited info on how it applies to a more "integrated" environment? ( with "integrated", I mean lots of other code running as well)

On top of it I have a bit of horror about char and Strings!

What I need to do, is to communicate (full duplex) between 2 Arduino Mega. I have the code somewhat sorted but it does not performvery well. I'm sure I just need to cut n copy n paste some lines from somewhere to somewhere :o

If you could kindly slap me in the right direction, I'd be very much obliged. (I promise you some digital Beers! :slight_smile: )

Here is the "Master" code:

String MessageToSlave1 = "Message To Slave 1"; // Just a dummy String
String MessageToSlave2 = "Message To Slave 2"; // Just a dummy String
String MessageToSlave3 = "Message To Slave 3"; // Just a dummy String

const byte numChars = 500; // Should be enough but may change later
char receivedFromSlaveChars[numChars]; // descriptive names help my peanut brain

boolean newSlaveData = false; // Have we received data from Slave?

void setup() {
  Serial.begin(9600); // open 2 instances of IDE and allocate different COM ports
  Serial.println("<Arduino Master is ready>");
  Serial3.begin(9600); // begin Serial Port 3 on Mega
}

void loop() { // Thank you Robin2 on the forum for your SerialBasics tut
  recvWithStartEndMarkers(); // Check for <' & '\n' char
  showSlaveData(); // Reveal what the Slave has sent
  delay(1500); // Other thing happening in the meantime..... 1500 millis is an arbitrary number
  sendDataToSlave(); // send instructions (message) to Slave
}

void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (Serial3.available() > 0 && newSlaveData == false) {
    rc = Serial3.read();

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

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

void showSlaveData() {
  if (newSlaveData == true) {
    Serial.print("This just in from Slave: ");
    Serial.println(receivedFromSlaveChars);
    newSlaveData = false;
  }
}
void sendDataToSlave() {
    if (newSlaveData == false) {
      Serial3.print ('<'); // This is the Start Marker for msg 1
      Serial3.print(MessageToSlave1);
      Serial3.println ('>'); // This is the End Marker for msg 1
      Serial3.print ('<'); // This is the Start Marker for msg 2
      Serial3.print(MessageToSlave2);
      Serial3.println ('>'); // This is the End Marker for msg 2
      Serial3.print ('<'); // This is the Start Marker for msg 3
      Serial3.print(MessageToSlave3);
      Serial3.println ('>'); // This is the End Marker for msg 3
    }
}

And here is the "Slave code:

String MessageToMaster1 = "Message To Master 1"; // Just a dummy String
String MessageToMaster2 = "Message To Master 2"; // Just a dummy String
String MessageToMaster3 = "Message To Master 3"; // Just a dummy String

const byte numChars = 500; // Should be enough but may change later
char receivedFromMasterChars[numChars]; // descriptive names help my peanut brain

boolean newMasterData = false; // Have we received data from Master?

void setup() {
  Serial.begin(9600); // open 2 instances of IDE and allocate different COM ports
  Serial.println("<Arduino Slave is ready>");
  Serial3.begin(9600); // begin Serial Port 3 on Mega
}

void loop() { // Thank you Robin2 on the forum for your SerialBasics tut
  recvWithStartEndMarkers(); // Check for "<" char
  showMasterData(); // Reveal what the Master has sent
  delay(2000); // Other thing happening in the meantime..... 2000 millis is an arbitrary number
  sendDataToMaster(); // send info (message) to Master  
}

void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (Serial3.available() > 0 && newMasterData == false) {
    rc = Serial3.read();

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

    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}
void showMasterData() {
  if (newMasterData == true) {
    Serial.print("This just in from Master: ");
    Serial.println(receivedFromMasterChars);
    newMasterData = false;
  }
}

void sendDataToMaster() {
    if (newMasterData == false) {
      Serial3.print ('<'); // This is the Start Marker for msg 1
      Serial3.print(MessageToMaster1);
      Serial3.println ('>'); // This is the End Marker for msg 1
      Serial3.print ('<'); // This is the Start Marker for msg 2
      Serial3.print(MessageToMaster2);
      Serial3.println ('>'); // This is the End Marker for msg 2
      Serial3.print ('<'); // This is the Start Marker for msg 3
      Serial3.print(MessageToMaster3);
      Serial3.println ('>'); // This is the End Marker for msg 3
    }
}

I see that you are using Robin2's serial input basics code that reads data into a string (null terminated character array). Why are you messing with Strings. Do you understand the problems that the use of Strings can lead to?

What is the problem with the code? "Not performing" is not much of a description of a problem.

Is there a reason for using such a slow baud rate (9600)? With hardware serial you should be able to go over 200000 baud.

groundFungus:
I see that you are using Robin2's serial input basics code that reads data into a string (null terminated character array).

Yes... seemed like a good foundation to start off with :slight_smile:

groundFungus:
Why are you messing with Strings. Do you understand the problems that the use of Strings can lead to?

I kinda like Strings as I feel they are much easier to work with :o
I gather the use of Strings may lead to memory fragmentation yes.

groundFungus:
Is there a reason for using such a slow baud rate (9600)? With hardware serial you should be able to go over 200000 baud.

Because there are other devices in the overall project that run on 9600 and therefore I thought to keep it consistent throughout. (for now)

groundFungus:
What is the problem with the code? "Not performing" is not much of a description of a problem.

Oops sorry. I still wanted to include it in the OP.
With not Not performing I meant that the bi directional comms is not consistent.

Here is as sample of what appear in the "Master" serial monitor:

<Arduino Master is ready>
This just in from Slave: Message To Master 1
This just in from Slave: Message To Master 2
This just in from Slave: Message To Maste<Message To Master 1
This just in from Slave: Message To Master 1
This just in from Slave: Message To Master 1
This just in from Slave: Message To Master 2
This just in from Slave: Message To Mas<Message To Master 1
This just in from Slave: Message To Master 1
This just in from Slave: Message To Master 1
This just in from Slave: Message To Master 2
This just in from Slave: Message To Mas<Message To Master 1

Here is as sample of what appear in the "Slave" serial monitor:

<Arduino Slave is ready>
This just in from Master: Message To Slave 1
This just in from Master: Message To Slave 2
This just in from Master: Message To Slave 3<Message To Slave 1
This just in from Master: Message To Slave 1
This just in from Master: Message To Slave 1
This just in from Master: Message To Slave 2sage T<Message To Sl<Message To Slave 1
This just in from Master: Message To Slave 1
This just in from Master: Message To Slave 2
This just in from Master: Message To Slave<Message To Slave 1
This just in from Master: Message To Slave 1
This just in from Master: Message To Slave 1
This just in from Master: Message To Slave<Message To Slave 1
This just in from Master: Message To Slave 1
This just in from Master: Message To Slave 1
This just in from Master: Message To Slave<Message To Slave 1
This just in from Master: Message To Slave 1
This just in from Master: Message To Slave 1
This just in from Master: Message To Slave<Message To Slave 1
This just in from Master: Message To Slave 1
This just in from Master: Message To Slave 1

As you can see, message2 and message3 are not consistently transmitted.

I suspect that your delays in loop() are causing havoc; Robin's code does not work well with that. Robin has another thread about "doing multiple things at the same time"; search for it, it's a sticky.

The symptoms that you show indicate that you're missing data because the software buffer of the Serial class is not emptied often enough (your code does not see the '>' at occasion).

Do you need the slaves to only reply to the master after they received something? Or will they send data regardless of if they have received data from the master or not.

If the slaves should only react on commands from the master, you can change the master to something like below.

bool waitingForReply = false;

...
...

void loop()
{
  if(waitingForReply == false)
  {
    sendDataToSlave();
    waitingForReply = true;
  }

  recvWithStartEndMarkers();

  if(newSlaveData == true)
  {
    showSlaveData();
    waitingForReply = false;
  }
}

And in the slave, only send replies if you have received a full message from the master.

sterretje:
I suspect that your delays in loop() are causing havoc; Robin's code does not work well with that. Robin has another thread about "doing multiple things at the same time"; search for it, it's a sticky.

Yes, the "delay" is there just to simulate that other computation is going on in other parts of the program. Like communicating with GSM module on Serial1 and Nextion display on Serial2 and a whole lot of other stuff. :slight_smile:

sterretje:
The symptoms that you show indicate that you're missing data because the software buffer of the Serial class is not emptied often enough (your code does not see the '>' at occasion).

You are probably spot on there!

sterretje:
Do you need the slaves to only reply to the master after they received something? Or will they send data regardless of if they have received data from the master or not.

It would have been nice to give the slaves some freedom to say something to the master as they feel like it :slight_smile: :slight_smile:
But yes, it is probably the best way to only request info from the slave(s) if the master has a time slot available for them.

sterretje:
If the slaves should only react on commands from the master, you can change the master to something like below....

I shall duly play and tinker with your suggestion and will report back.

Thank you.

  delay(2000); // Other thing happening in the meantime..... 2000 millis is an arbitrary number

You can't let another thing happen for two whole seconds. The Serial buffer can only hold a few milliseconds worth of data before it overflows.

So every part of your program must return back to loop in just a few milliseconds or microseconds. Usually that is easy to do.

The delay() function should be used very rarely. I probably use one delay in 10,000 lines of Arduino code.

Chilli_Paste:
If you could kindly slap me in the right direction, I'd be very much obliged. (I promise you some digital Beers! :slight_smile: )

The long delay()s are going to screw everything up because the timing of the two programs will get out of sync. A receiving program should never have a delay() and in this case both programs are receiving programs.

I can understand the use of a delay() to simulate the time taken by other program code but 1.5 or 2 seconds is a very long time.

If the programs need a considerable amount of time to do other work then you need some means to co-ordinate the communication between the programs - for example a program only sends data when requested to do so.

If you arrange for the largest single message to fit within the 64 byte Serial Input Buffer then a program can read the data and then signal that more data can be sent. And while the data is being sent it can be doing other stuff - indeed it could spend as long as it wishes doing other stuff. When the other stuff is finished the new data should be waiting in the Buffer.

All of this can work with my example code.

...R

I give up! Sheez this is not as easy as I would have thought. My brain is smoking :slight_smile:

I'll probably have to go and use a digital pin and set it to high or low to tell the Slave "OK to transmit".
And whilst the master has set this pin high, it should stop doing whatever it was doing and friggen listen for slave data!

If you see a towel there somewhere..... It could be mine that I've thrown in :slight_smile: :slight_smile: :slight_smile:

I referred earlier to another thread by Robin; below the link
Demonstration code for several things at the same time

sterretje:
I referred earlier to another thread by Robin; below the link
Demonstration code for several things at the same time

Dankie Sterrejie. Jy's 'n Doring! :slight_smile: (I do think you will understand as your nickname suggest you understand Afrikaans or Dutch)

I do have a comprehension of "doing several things at the same time". But I somehow cannot see how that relates to the Serial comms question.

Perhaps it would help if you see a sample of the current "unrefined" code that is currently running on one of the controllers. (I elected to rather attach the ino file than "litter" here with over a thousand lines of code. :slight_smile:

Also attached see a picture of the current setup.

Now I'm working on the expansion of the loggers to gather more environmental data.
All this just because I'm too lazy to drive behind the mountain every day to check on the borehole pump!!!) :slight_smile:

sample.ino (63.4 KB)

Chilli_Paste:
I'll probably have to go and use a digital pin and set it to high or low to tell the Slave "OK to transmit".

That is certainly an option but I reckon it would be simpler for the master to do Serial.println(""); when it is OK to print.

...R

Why do you need the slave? Run out of pins, memory, CPU time?

wildbill:
Why do you need the slave? Run out of pins, memory, CPU time?

The Slave will be attached to the main board Expansion port to get additional data like:
Water table of borehole
Tank level status
Weather sensors
Wifi (IoT)
etc
etc

Right now I'm just using another logger to test the serial comms.

"Run out of pins"......Me? NEVER! :slight_smile: :slight_smile: :slight_smile:

Robin2:
That is certainly an option but I reckon it would be simpler for the master to do Serial.println(""); when it is OK to print.

...R

Thank you Robin. If only my little peanut brain could wrap its head around that concept, Life would be one big JellyTot for me! (well I do understand the concept, but to turn THAT into code!)

I have now come up with a McGiver plan to achieve what I need to do. (It certainly needs some refinement but the concept works)

Herewith the Master:

#define HeySlavePin 22 // Hey! phuuug! I want some data!
#define SlaveHasSomethingToSayToMePin 13 // Aaargh what the master want from me now again?

String MessageToSlave1 = "Message To Slave 1"; // Just a dummy String
String MessageToSlave2 = "Message To Slave 2"; // Just a dummy String
String MessageToSlave3 = "Message To Slave 3"; // Just a dummy String

String MessageFromSlave1;
String MessageFromSlave2;
String MessageFromSlave3;

void setup() {
  Serial.begin(9600); // open 2 instances of IDE and allocate different COM ports
  Serial.println("<Arduino Master is ready>");
  Serial3.begin(9600); // begin Serial Port 3 on Mega
  pinMode(HeySlavePin, OUTPUT);
  digitalWrite(HeySlavePin, HIGH);
  pinMode(SlaveHasSomethingToSayToMePin, INPUT);
}

void loop() {

  delay(500); // PLEASE NOTE: This is just for simulation of other processes that happen in the rest of the code
  digitalWrite(HeySlavePin, LOW);

  while (digitalRead(SlaveHasSomethingToSayToMePin) == LOW) {
    digitalWrite(HeySlavePin, HIGH);
    while (Serial3.available()) {
      MessageFromSlave1 = Serial3.readString();
      if (MessageFromSlave1.indexOf('\n')) {
        Serial.println(MessageFromSlave1);
        MessageFromSlave1 = " ";
      }
      MessageFromSlave2 = Serial3.readString();
      if (MessageFromSlave2.indexOf('\n')) {
        Serial.println(MessageFromSlave2);
        MessageFromSlave2 = " ";
      }
      MessageFromSlave3 = Serial3.readString();
      if (MessageFromSlave3.indexOf('\n')) {
        Serial.println(MessageFromSlave3);
        MessageFromSlave3 = " ";                
      }      
    }
    Serial3.println(MessageToSlave1);
    Serial3.println(MessageToSlave2);
    Serial3.println(MessageToSlave3);        
  }
} // end of Loop

And here the Slave:

#define MasterIsBotheringMePin 13 // Aaargh what the master want from me now again?
#define HeyMasterPin 22 // Hey! here's the requested data!


String MessageFromMaster1 = "";
String MessageFromMaster2 = "";
String MessageFromMaster3 = "";

String MessageToMaster1 = "Message To Master 1"; // Just a dummy String
String MessageToMaster2 = "Message To Master 2"; // Just a dummy String
String MessageToMaster3 = "Message To Master 3"; // Just a dummy String


void setup() {
  Serial.begin(9600); // open 2 instances of IDE and allocate different COM ports
  Serial.println("<Arduino Slave is ready>");
  Serial3.begin(9600); // begin Serial Port 3 on Mega  
  pinMode(HeyMasterPin, OUTPUT);
  digitalWrite(HeyMasterPin, HIGH);
  pinMode(MasterIsBotheringMePin, INPUT);
}

void loop() {
  delay(500);
  if (digitalRead(MasterIsBotheringMePin) == LOW) {
    digitalWrite(HeyMasterPin, LOW);
    Serial3.println(MessageToMaster1);
    Serial3.println(MessageToMaster2);
    Serial3.println(MessageToMaster3);   
  }
  else if (digitalRead(MasterIsBotheringMePin) == HIGH) {
    digitalWrite(HeyMasterPin, HIGH);
    while (Serial3.available()) {
      MessageFromMaster1 = Serial3.readString();
      if (MessageFromMaster1.indexOf('\n')) {
        Serial.println(MessageFromMaster1);
        MessageFromMaster1 = " ";
      }
      MessageFromMaster2 = Serial3.readString();
      if (MessageFromMaster2.indexOf('\n')) {
        Serial.println(MessageFromMaster2);
        MessageFromMaster2 = " ";
      }
      MessageFromMaster2 = Serial3.readString();
      if (MessageFromMaster2.indexOf('\n')) {
        Serial.println(MessageFromMaster2);
        MessageFromMaster2 = " ";                
      }      
    }

    
  }
}

Thank you all for your valuable input and here's a round of digital Beers to you all as promised!

I know that using the String (capital S) class is convenient - that's why it was created. But it can cause memory corruption in the small memory on an Arduino. This can happen after the program has been running perfectly for some time. Just use cstrings - char arrays terminated with '\0' (NULL).

...R

Robin2:
I know that using the String (capital S) class is convenient - that's why it was created. But it can cause memory corruption in the small memory on an Arduino. This can happen after the program has been running perfectly for some time. Just use cstrings - char arrays terminated with '\0' (NULL).

...R

Robin, I hear ya. You are much more knowledgeable than me on this subject. But... (only in my my defence :slight_smile: ) the cstring thing appears sooooooo convoluted to me that I chose the easy way out. And that even meant forking out a few more bucks and use the Mega instead with plenty of memory.(Oh that tiny Mega Pro (embed) board is soooo sexy). I have one datalogger running for over a month now non-stop with the sample code I attached previously)

If only there was a simple way to use char strings (without all the ++ here and -- there and and pointers and * and peeking and god knows what) then yes I'd be the first to adopt it.
Bad habits die hard and be honest.... even you would reach for a 555 timer if you only need a simple flip flop ey? :slight_smile:

Compared to a PC a Mega still has almost no memory.

However, you have been warned and if it works during all your tests and then fails after 6 days of normal running you will know what to look for :slight_smile:

...R

Chilli_Paste:
Dankie Sterrejie. Jy's 'n Doring! :slight_smile: (I do think you will understand as your nickname suggest you understand Afrikaans or Dutch)

I'm Dutch and live in South Africa (now about 20 years); best of both worlds :smiley: