SoftwareSerial: Bytes not sent in a round trip between two Arduino's

Hello,

I am trying to figure out how to communicate between two devices with the SoftwareSerial library. I was first trying to echo a byte from the transmitter to the receiver of the same SoftwareSerial object, but apparently it doesn't seem to be possible.
I made some progress with two Arduino UNO. Let's call them the left-hand and right-hand devices. The left-hand device waits for an input byte from the Serial console. When it gets one, it sends it to the right-hand device which sends it back to the left-hand device. The left-hand device then acknowledges the receiving of the byte, thus confirming the round-trip.
Pin 10 of the left-hand device is connected to pin 11 of the right-hand device.
Pin 11 of the left-hand device is connected to pin 10 of the right-hand device.

Left-hand device sketch:
#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX

void setup()
{
// Open serial communications and wait for port to open:
Serial.begin(57600);
// set the data rate for the SoftwareSerial port
mySerial.begin(9600);
}

void loop() // run over and over
{
if (mySerial.available()) // A byte is arriving after a round-trip
{
char c = mySerial.read();
Serial.print("Received ");
Serial.println(c);
}
if (Serial.available())
{
char c = Serial.read();
Serial.print("Sending ");
Serial.println(c);
mySerial.write(c); // Send the byte for a round-trip
}
}

Right-hand device sketch:
#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX

void setup()
{
// Open serial communications and wait for port to open:
Serial.begin(57600);
// set the data rate for the SoftwareSerial port
mySerial.begin(9600);
}

void loop()
{
if (mySerial.available()) // A byte has arrived
{
char c = mySerial.read();
Serial.print("Received ");
Serial.println(c);
mySerial.write(c); // Send it back
}
}

Problem: When I input a string such as "RUN FORWARD" to the Serial console of the left-hand device, this is a typical result I get:
Sending R
Sending U
Received R
Sending N
Received U
Sending W
Received N
Sending A
Received W
Sending R
Received A
Sending D
Received R
Received D

Only "RUNWARD" was sent (and I received "RUNWARD"). Why didn't all the bytes were sent?
If you have other remarks by-the-way about SoftwareSerial (or any method to communicate between microcontrolers) and how to use it, it will be mostly appreciated!

As far as I understand it, SoftwareSerial disables interrupts during transmission which is why it can't send and receive at the same time. This is why you can't look it back to itself. It also means that input via the hardware Serial interface is going to be disabled (briefly) each time SoftwareSerial transmits a byte and might explain why you seem to be losing characters occasionally.

I think you might have more success if the sketch reading from the hardware Serial port waited until it had received a complete line before it sent it over the SoftwareSerial port, and the 'right hand' sketch waited until it had received the complete line before it echoed it back.

Hello PeterH,
Thank you for your feedback. It helped me debug the program. These are the sketches I used to get a correct behavior:

Left-hand device sketch:

#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX

void setup()
{
// Open serial communications and wait for port to open:
Serial.begin(57600);

// set the data rate for the SoftwareSerial port
mySerial.begin(9600);
mySerial.println("Hello, world?");
}

void loop() // run over and over
{
if (mySerial.available())
{
String receivedMessage;
while (mySerial.available())
{
char c = mySerial.read();
receivedMessage += c;
delay(1);
}
Serial.print("Received ");
Serial.println(receivedMessage);
}
if (Serial.available())
{
String inputMessage;
while (Serial.available())
{
char receivedChar = Serial.read();
inputMessage += receivedChar;
delay(1);
}

Serial.print("Sending ");
Serial.println(inputMessage);
mySerial.println(inputMessage);
}
}

Right-hand device sketch:

#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX

void setup()
{
// Open serial communications and wait for port to open:
Serial.begin(57600);
// set the data rate for the SoftwareSerial port
mySerial.begin(9600);
}

void loop()
{
if (mySerial.available())
{
String receivedMessage;
while (mySerial.available())
{
char c = mySerial.read();
receivedMessage += c;
delay(1);
}
mySerial.println(receivedMessage);
}
}

This is a typical result I get through the console:

Sending 1 2 3 4 5 6 7 8 9 10
Received 1 2 3 4 5 6 7 8 9 10

Sending This is a very long sentence to test the communication
Received This is a very long sentence to test the communication

Notice the use of delay(1); after reading a character. If this line is removed, inconsistent behaviors are observed (such as a string cut in multiple parts).

  1. When posting code, please PLEASE use tags.

  2. Using "delay()" to wait for serial characters to arrive is going to eventually byte you. Instead, wait until some end-of-line marker is received.

Hello James,
Thank you for your comment and suggestion.
I implemented code in which I use an end-of-message character:

Left-hand device sketch:

#include <SoftwareSerial.h>
#define END_OF_MESSAGE_CHAR '|'

SoftwareSerial mySerial(10, 11); // RX, TX

void setup()  
{
  // Open serial communications and wait for port to open:
  Serial.begin(57600);
  // set the data rate for the SoftwareSerial port
  mySerial.begin(9600);
}

void loop() // run over and over
{
  if (mySerial.available())
  {
    String inputMessage;
    char readChar = mySerial.read();
    bool endOfMsgCharWasReceived = (readChar == END_OF_MESSAGE_CHAR);
    bool aNewCharacterIsRead = true;
    while(!endOfMsgCharWasReceived)
    {
      if (aNewCharacterIsRead)
        inputMessage += readChar;
      aNewCharacterIsRead = false;
      if (mySerial.available())
      {
        readChar = mySerial.read();
        endOfMsgCharWasReceived = (readChar == END_OF_MESSAGE_CHAR);
        aNewCharacterIsRead = true;
      }
    }
    Serial.print("Received ");
    Serial.println(inputMessage);
  }
  if (Serial.available())
  {
    String inputMessage;
    char readChar = Serial.read();
    bool endOfMsgCharWasReceived = (readChar == END_OF_MESSAGE_CHAR);
    bool aNewCharacterIsRead = true;
    while(!endOfMsgCharWasReceived)
    {
      if (aNewCharacterIsRead)
        inputMessage += readChar;
      aNewCharacterIsRead = false;
      if (Serial.available())
      {
        readChar = Serial.read();
        endOfMsgCharWasReceived = (readChar == END_OF_MESSAGE_CHAR);
        aNewCharacterIsRead = true;
      }
    }
    inputMessage += END_OF_MESSAGE_CHAR;
    Serial.print("Sending ");
    Serial.println(inputMessage);
    mySerial.print(inputMessage);
    
  }
  
}

Right-hand device sketch:

#include <SoftwareSerial.h>
#define END_OF_MESSAGE_CHAR '|'

SoftwareSerial mySerial(10, 11); // RX, TX

void setup()  
{
  // Open serial communications and wait for port to open:
  Serial.begin(57600);
  // set the data rate for the SoftwareSerial port
  mySerial.begin(9600);
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
  digitalWrite(13, HIGH);
  digitalWrite(12, LOW);
}

void loop()
{
  if (mySerial.available())
  {
    Serial.println("mySerial.available()");
    digitalWrite(13, LOW);
    String inputMessage;
    char readChar = mySerial.read();
    bool endOfMsgCharWasReceived = (readChar == END_OF_MESSAGE_CHAR);
    bool aNewCharacterIsRead = true;
    while(!endOfMsgCharWasReceived)
    {
      digitalWrite(12, HIGH);
      if (aNewCharacterIsRead)
        inputMessage += readChar;
      aNewCharacterIsRead = false;
      if (mySerial.available())
      {
        readChar = mySerial.read();
        endOfMsgCharWasReceived = (readChar == END_OF_MESSAGE_CHAR);
        aNewCharacterIsRead = true;
      }
    }
    digitalWrite(12, LOW);
    inputMessage += END_OF_MESSAGE_CHAR;
    Serial.print("inputMessage = ");
    Serial.println(inputMessage);
    mySerial.print(inputMessage);
  }
  digitalWrite(13, HIGH);
 
}

Typical results:

Sending 1234|
Received ÿÿ1234
Sending 434 3434|
Received 434 3434
Sending this is a test!|
Received this is a test!

Note 1: The first message has a few corrupted bytes.
Note 2: In order for it to work, I had to connect Vin of both devices together and GND of both devices together (i.e. the right-hand device was powered by the left-hand device). If I powered the two devices independently (for instance powering the right-hand device with a battery and the left-hand device through the USB cable), I could not make it work.
I assume Note 2 is related to the need for both devices to have common logic levels in order to be able to communicate together.

Once again, all comments are welcome!

If using two different power sources, you absolutely must connect GND together.