Software Serial - Delay needed

I have been Fiddling about (for ages....) trying to get software serial to talk between two UNO's .
For various reasons I'm running these from within "setup()" ; now working but the delays shown in the code are needed for it to work; if I take either of them out " recvWithEndMarker();" is not called or that code doesn't run.
Why are the delays needed for it to work,?

//software serialplay1
//TRANSMITTER
#include <SoftwareSerial.h>
const byte startMarker = 0x3C;
const byte endMarker = 0x3E;
byte SEND[4] = {0xFF, 0x10, 0x22, 0x33};
/*
SEND[4]==
0 Data: 255 Decimal
1 Data: 16
2 Data: 34
3 Data: 51
 */
SoftwareSerial mySerial(4,3); // RX, TX
 
void setup()
{
  // Open serial communications and wait for port to open:
  mySerial.begin(19200);
  Serial.begin(9600);
  Serial.println ("OK !");
  Serial.println("");
  for (int i=0; i<=3;i++)
  {
    Serial.print (i);
   Serial.print(" Data: ");
   Serial.println (SEND[i], DEC);
    
  }
  delay(200);
mySerial.write(startMarker);
mySerial.write(startMarker);
  for (int i = 0; i <= 3; i++)
  {
    mySerial.write(SEND[i]);
  }
  mySerial.write(endMarker);
}

void loop()
{
}
                    and....
//software serialplay6
//RECEIVER.

#include <SoftwareSerial.h>
SoftwareSerial mySerial(3, 4); // RX, TX
const byte numBytes = 8;
byte recievedBytes[numBytes];   // an array to store the received data
boolean newData = false;
byte startmarkerCount = 0;
const byte startMarker = 0x3C;
const byte endMarker = 0x3E;
static boolean recvInProgress = false;

void setup()
{
  Serial.begin(9600);
  mySerial.begin(19200);
  Serial.println("<Arduino is ready>");
  while (mySerial.available() == 0)
    delay(8);// at least 5 needed
  recvWithEndMarker();
  showNewData();
}


void loop()
{
}

void recvWithEndMarker()
{
  delay(3);// at least 2 needed
  static byte ndx = 0;
  byte rc;

  while (mySerial.available() > 0)
  {
    rc = mySerial.read();

    if (recvInProgress == true)
    {
      if (rc != endMarker)
      {
        recievedBytes[ndx] = rc;
        ndx++;
        if (ndx >= numBytes)
        {
          ndx = numBytes - 1;
        }
      }

      if (rc == endMarker)
      {
        //Serial.print("IN else");
        recievedBytes[ndx] = '\0'; // terminate the string
        ndx = 0;
        recvInProgress == false;
        newData = true;
      }
      //***********
    }
    else if ((rc == startMarker) && (recvInProgress == false))
    {
      delay(2);// needs at least 1mS delay !!
      if ((rc = mySerial.read()) == startMarker)// check next one.
      {
        recvInProgress = true;
        ndx = 0;
      }
      else
      {
        startmarkerCount = 0;
      }
    }
  }
}
//*************************************************************
void showNewData()
{
  if (newData == true)
  {
    Serial.println("This just in ... ");
    for (int i = 0; i <= 3; i++)
    {
      Serial.print (i);
      Serial.print (" Data =");
      Serial.println(recievedBytes[i]);
    }
  }
  newData = false;
}

The output on the serial monitor being correct and ...

<Arduino is ready>
This just in ... 
0 Data =255
1 Data =16
2 Data =34
3 Data =51

which arduino do you start first ?

ideally you would have some sort of handshake

Main arduino sends "Hello" every few seconds until it gets a "I'm here" message back. Then you know both arduinos are ready and they can talk

Because without them the receiver code calls recvWithEndMarker() as soon as a single byte is available to read. Adding a delay() gives time for the full message to arrive before trying to read it

In Robin's original examples recvWithEndMarker() is called repeatedly while something is available to be read and only ends when the end marker is received

By the way, why do this ?

mySerial.write(startMarker);
mySerial.write(startMarker);

This sort of communication synchronization can be done easily, if one builds a state machine in the code at each end, such that proceeding to process a message in either direction can only be done in controlled circumstances.
Getting there, of course, is most of the fun!

I think that there is a note on the software serial reference page that you can not send and receive at the same time; is that your problem?

There are a few alternative software serial libraries that alliw simultaneous send and receive.

in the data being sent, in the real code, there is a small chance of the
data being sent/received being the same as the start or end maker. The chances of two sets of such data appearing together is smaller . In this minimalised code its crept in by mistake.

Yes, I wanted to do some form of handshaking , I agree it would be more robust, but I've run out of pins .

Why not do it in loop() ?

it was for a setup of scaling parameters , and i wanted to do it in setup - during this post Ive abandoned that as I cant get it to work and have moved it to loop and trying to manipulate ( double use) some digitals to signal when to send data etc to make a better job of it .

So for now ( I'll be back ....) I'll make this as closed as you answered the inital question on delays.

thx

You could but would need a while loop in the setup and a sync strategy

I was doing that , but for some unknown reason I couldn’t get it to work with software serial .

In a while loop it wouldn’t do the “ serial.available “ bit . Possibly (?) my errors , but in loop working …Nearly done in loop now , so no longer an issue I’m looking to solve !
( if at first you don’t succeed , do something else ).

(I didn’t have a sync method , but ensured the receiver was ahead of the transmitter, waiting in a while loop for buffered data ).

while ( mySerial.available()==O)
delay  (100);
Etc etc 

As previously noted when you used a while loop in setup()

Thus the exit condition for the while loop was immediately satisfied

I did try a delay too to allow all message to be received .
As said , not sure why it didn’t work - you (@UKHeliBob ) advised the delay , but now moved on , hey ho .

Did I really advise the use of delay() ?

What I did was to explain why your code didn't work without it

OK that would solve half of the challenge :slight_smile:

[quote="UKHeliBob, post:14, topic:1277545"]
Did I really advise the use of delay() ?
[/quote] post 3

Thought it was you.. to give time for the whole message to be received …

This was in the receiver code in your original post

  while (mySerial.available() == 0)
    delay(8);// at least 5 needed
  recvWithEndMarker();

So nobody in this topic had a chance to suggest the use of delay()

May be the easiest way is to show you what we mean

wire your two UNOs this way

UNO 1 UNO 2 COMMMENT
D2 D3 SoftwareSerial connection (UNO 1 Rx to UNO 2 Tx)
D3 D2 SoftwareSerial connection (UNO 1 Tx to UNO 2 Rx)
GND GND ensure common ground between boards

on UNO 1, upload this code:

#include <SoftwareSerial.h>
SoftwareSerial remoteSerial(2, 3);  // Rx on 2, Tx on 3 (crossed cables, ensure GND are connected)

const uint8_t maxMessageSize = 50;
char remoteMessage[maxMessageSize + 1]; // +1 for trailing null

boolean getRemoteMessage(bool forceReset = false, const char endMarker = '\n') {
  static byte currentPosition = 0;
  boolean messageIsReady = false;

  if (forceReset) {
    remoteMessage[0] = '\0';
    currentPosition = 0;
  } else {
    int r = remoteSerial.read();
    if (r != -1) {
      if (r == endMarker) {
        if (currentPosition == 0) remoteMessage[0] = '\0';
        currentPosition = 0;
        messageIsReady = true;
      } else {
        remoteMessage[currentPosition++] = r;
        remoteMessage[currentPosition] = '\0';
        if (currentPosition >= maxMessageSize) currentPosition = maxMessageSize - 1; // don't overflow overwrite the last char
      }
    }
  }
  return messageIsReady;
}


void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);
  remoteSerial.begin(9600);

  Serial.println("UNO 1 Ready");

  unsigned long chrono = millis() - 1000; // -1000 so that we send the attention message right away
  while (true) {// wait for ACK - blocking if no ACK
    if (millis() - chrono >= 1000) {
      Serial.println("Sending Attention message.");
      getRemoteMessage(true);     // force reset the listener
      remoteSerial.write("AT\n"); // send the attention message again
      remoteSerial.flush();
      chrono = millis();
    }

    if (getRemoteMessage()) {
      if (strcmp(remoteMessage, "ACK") == 0) {
        Serial.println("Received ACK, Proceeding to loop");
        break;
      } else {
        Serial.print("Wrong ACK => "); Serial.println(remoteMessage);
      }
    }
  } // end of infinite loop waiting for "ACK"
}

void loop() {
  // blink the builtin led to show we are now in the loop
  digitalWrite(LED_BUILTIN, HIGH);
  delay(500);
  digitalWrite(LED_BUILTIN, LOW);
  delay(250);
}

on UNO2 upload this code

#include <SoftwareSerial.h>
SoftwareSerial remoteSerial(2, 3);  // Rx on 2, Tx on 3 (crossed cables)

const uint8_t maxMessageSize = 50;
char remoteMessage[maxMessageSize + 1]; // +1 for trailing null

boolean getRemoteMessage(const char endMarker = '\n') {
  static byte currentPosition = 0;
  boolean messageIsReady = false;

  int r = remoteSerial.read();
  if (r != -1) {
    if (r == endMarker) {
      if (currentPosition == 0) remoteMessage[0] = '\0';
      currentPosition = 0;
      messageIsReady = true;
    } else {
      remoteMessage[currentPosition++] = r;
      remoteMessage[currentPosition] = '\0';
      if (currentPosition >= maxMessageSize) currentPosition = maxMessageSize - 1; // don't overflow overwrite the last char
    }
  }
  return messageIsReady;
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);
  remoteSerial.begin(9600);
  Serial.println("UNO 2 Ready");

  while (true) {// wait for AT - blocking
    if (getRemoteMessage()) {
      if (strcmp(remoteMessage, "AT") == 0) {
        Serial.println("Received AT, Responding with ACK and Proceeding to loop");
        remoteSerial.write("ACK\n");
        break;
      } else {
        Serial.print("Wrong ATTENTION MESSAGE => "); Serial.println(remoteMessage);
      }
    }
  } // end of infinite loop waiting for "AT"
}

void loop() {
  // blink the builtin led to show we are now in the loop
  digitalWrite(LED_BUILTIN, HIGH);
  delay(500);
  digitalWrite(LED_BUILTIN, LOW);
  delay(250);
}

Now

  • When you boot UNO1, it will keep trying sending an attention message ("AT\n") on its SoftwareSerial port until it gets an "ACK\n" message back.
  • When you boot UNO2, it will wait for an an attention message ("AT\n") on its SoftwareSerial port and will send an "ACK\n" message back.

both UNO stays in the setup() until the AT message has been ACK-ed.

Once this handshake is done, both proceed to the loop and blink the builtin led.

Does that help?

Thanks for that ; I’ll run that a bit later.
Looks very useful .

EDIT: ****** looked at this ,interesting method, not something I would have imagined! -thx*****

No true , but you explained why a delay was required and why it didn't work without !
implication == you need delay.

Q. was: "Why are the delays needed for it to work,?"

over and out