Sending byte array over Serial comm

I am trying to find a robust way of sending 2 bytes from one Arduino to another. I have tried sending an array with a start value, the two subject bytes followed by an end byte. (The markers are reserved and will not be sent as data.)

//mega on com6 sender
#include<SoftwareSerial.h>

#define RXPIN 3
#define TXPIN 4
SoftwareSerial SUART(RXPIN,TXPIN);  
byte myArr[] = {254,210,1, 255};

void setup() 
{
  Serial.begin(9600);
  SUART.begin(9600);
  delay(1000); // tried with  and without a delay
  SUART.write(myArr,4);
}
void loop() 
{
//nano on com7 receiver
#include<SoftwareSerial.h>

#define RXPIN 7
#define TXPIN 8
#define START_BYTE 254
#define END_BYTE   255
byte rcvdArr[4];
const unsigned int MAX_MSG_LENGTH = 4; 
SoftwareSerial Suart(RXPIN,TXPIN); 

void setup()
{
  Serial.begin(9600);
  Suart.begin(9600);
  delay(1000);    // tried with and without this
}
void loop() {
  
  if( Suart.available() != 0) {
    int length =  Suart.readBytesUntil(END_BYTE, rcvdArr, 4);
    Serial.print("length  ");
    Serial.println(length);
    if (length == 4 && rcvdArr[0] == START_BYTE) {// END_BYTE discarded, length var still 4 
      Serial.println(rcvdArr[1]);
      Serial.println(rcvdArr[2]);
   } else {
      Serial.println("bad packet");
    }
  }
}

This is the output

length 1
bad packet
length 4
210
1

The sketch sends only one packet by intention but before that comes through there is something else which is rejected (the length 1 return is a 0 for invalid data). I don't know what the invalid data is but it would be disruptive if I drop this into the project sketch I am working on. Before I get into a more elaborate model sketch can anyone tell me what might be causing this? What is the stowaway? Would I avoid it by using Serial.read() in a for loop?

explain how things are wired and how you are testing (and which arduinos you are using)

1 Like
  if( Suart.available() != 0) {
    int length =  Suart.readBytesUntil(END_BYTE, rcvdArr, 4);
    Serial.print("length  ");
    Serial.println(length);

Does this portion of code read 4 bytes into an int variable and what has the value of that variable got to do with the length of the message ?

Which Arduino boards are you using ?

To be honest, we don't care about wrong data. We don't use the .readBytesUntil() function.

Can you describe the protocol ? If I ask someone to describe it, than most fail to do that.

This is what you have:

start byte: 254
end byte: 255
two databytes in the middle.

But that is not enough.
Can the data bytes be 254 or 255 as well ?
Is there any CarriageReturn or LineFeed after it ? If you are not sure, how do you deal with that ?
Is there a timeout ? Suppose there is no data for a few seconds, and the first byte is 254, will that resync the reading of the data ?

Tips:

  1. In the receiver, always read every byte. Put them in a buffer. If you think that a complete set is in the buffer, then process the buffer. For better syncing, you could make the buffer 4 bytes and shift the buffer each time new data is added to the buffer. This is only possible if there is a unique start byte, or else it is better not to resync so easily.
  2. The AltSoftSerial works better. It is a lot better for low baudrates, but not for 115200 baud.
  3. To prevent syncing problems, you could send readable text with the bytes, or use a "escape" character.

I have a Mega 2560, the sender and a Nano, the receiver both connected by USB to my PC (for power and uploading only). The serial connection between the Arduinos is by software serial through the Tx/Rx pins defined in the sketch and cross wired. I am just testing by reseting the sender which has a specimen array in its globals.

Maybe not relevant but I just tried it deleting the 1, from that array and I get the same output (except it's 210, 255 instead of 210, 1 which means the line: if (length == 4 && rcvdArr[0] == START_BYTE) is ineffective and the END_BYTE is not discarded. )

add a wire between the GNDs

On the MEGA you could use Serial1 / Serial2 / Serial3 rather than softwareSerial

the listener should obviously be booted up before you reset the other arduino

1 Like

In view of that, please explain what this line of code does

 int length =  Suart.readBytesUntil(END_BYTE, rcvdArr, 4);

I am not entering the data from the keyboard so I don't see how CR/LF could be added. I tried sending char array with escape character but it is cumbersome. The 2 raw bytes in this example are sent as '2', '1', '0' , '1' but the transmitted array will vary in length. (It maybe does anyway which is why I was trying to identify good data as 4bytes, start 254, end 255 and discard anything else. I think it is doing that but every packet is preceded by bad data. )

This is what I am working to:

Serial.readBytesUntil() returns the number of characters read into the buffer. A 0 means that the length parameter <= 0, a time out occurred before any other input, or a termination character was found before any other input.

Now I read it again I am wondering what 'before any other input ' means in the context of termination character.

Thank you. I misunderstood what you were doing

Sorry I omitted to mention the GND wire. Your point on using the hardware Serial pins on Mega note and also about booting up the receiver first. At my stage of learning that actually isn't obvious.

Read this tutorial for a robust method: Serial Input Basics - updated - Introductory Tutorials - Arduino Forum

1 Like

I didn't explain that well; I meant IF I use a char array instead of raw bytes the array is of variable length and more complicated to process. As I am doing it now, sending by Serial.write(arr,length) I though it should send 4 bytes ( but actually still sends if you present it with an array of 3 or 5 or 6 bytes ). On the receiving end, the array is truncated to 4 bytes which will only include the END byte if it is exactly 4 bytes. The mysterious bad data is actually 4 zeros sent ahead of every good package and I can't find where this is coming from. It may be what JML said about booting the listener before the sender. I can't really do that with this model so I will have to try another.

Yes, I started from his example 6 and have rather digressed. I have been trying to get the readBytesUntil() approach working for too long now so maybe worth going back over that ground.

1 Like

If the listener is not booted up when the sender writes to the serial line then all bets are off. Your arduinos take a bit of time to boot up and configure the serial line.

@donw377
1. Check that your MEGA and NANO are connected by the Serial lines as per Fig-1.


Figure-1:

2. From your post #1, it is found that you want to send the elements of the following array to the receiver. You declare the data items in decimal; they are actually stored in memory as bits (I have shown the corresponding hex digits for conveniemce).

byte myArr[] = {254,210,1, 255};
==> byte myArr[] = [0xFE, 0xD2, 0x01, 0xFF);   //

3. In Step-2, it is seen that you are sending data items as natural binary and NOT ASCII codes which are usually done in Serial Communication.

4. In post #1, you have said for a robust way which means that you want the data to be received reliably by the NANO.

5. When sending natural binary bytes over Serial Port, usually a 3-byte wide synchronization pattern (say: 0x12, 0x34, 0x56; also called preamble or marker) is sent first, and then an index representing number of data bytes (in your case, it is 0x04), then the data bytes. It is very unlikey that three consective data bytes of the message would 0x12, 0x34, and 0x56. If it happens, the receiver will come up with wrong/no data. (The recommended way is to send ASCII-coded data items though it is slower compare to binary transmission.)

6. Based on Step-5, we can write the following sketches:
For MEGA Sender:

byte myArr[] = {254, 210, 1, 255}; //byte myArr[] = [0xFE, 0xD2, 0x01, 0xFF);

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

void loop()
{
  Serial1.write(0x12);
  Serial1.write(0x34);
  Serial1.write(0x56);
  //----------------------
  Serial1.write(0x04);
  //-----------------------
  Serial1.write(myArr, sizeof myArr);
  //----------------------
  delay(1000); //test interval
}

For NANO Receiver:

#include<SoftwareSerial.h>
SoftwareSerial SUART(7, 8); //SRX = 7, STX = 8
byte myData[4];

void setup()
{
  Serial.begin(9600);
  SUART.begin(9600); //for SUART, Bd = 9600 works well
}

void loop()
{
  byte n = SUART.available(); //checking if data coming
  if (n != 0)
  {
    if (n >= 3)  //Edit: before it was if(n == 3); see post#18
    {
      Serial.println("Checking for sync patt.");
      byte n1 = SUART.read();
      byte n2 = SUART.read();
      byte n3 = SUART.read();
      uint32_t n4 = (uint32_t)n1 << 16 | n2 << 8 | n3;
      if (n4 == 0x123456)
      {
        Serial.println("Sync patt found.");
        byte p = SUART.read();  //read the index
        byte m = SUART.readBytes(myData, p);  //read p number data bytes
        Serial.println("Data bytes received.");
        Serial.print(myData[0], DEC);  //shows: 254
        Serial.print(' ');
        //---------------------
        Serial.print(myData[1], DEC);  //shows: 210
        Serial.print(' ');
        //-----------------------
        Serial.print(myData[2], DEC);  //shows: 1
        Serial.print(' ');
        //--------------------------------
        Serial.println(myData[3], DEC);  //shows: 255
        Serial.print(' ');
        //--------------------------------
        Serial.println("======================");
      }
    }
  }
}

7. Output:

======================
Checking for sync patt.
Sync patt found.
Data bytes received.
254 210 1 255
 ======================
Checking for sync patt.
Sync patt found.
Data bytes received.
254 210 1 255
 ======================

1 Like

how is this any different than sending an ASCII string terminated with a newline using Serail.readlBytesUntil() and waiting until Serial.iavailable()?

There is no guarantee that this will be true. You could conceivable have received the preamble and some of the data by the time you check such that n > 3 in which case it will not work. Much better to change that test to n >= 3.

This is especially true in a real program where loop() would be much larger and potentially doing many other things.

1 Like

I have understood your logic, and I have edited my sketch of poat #16 accordingly after testing the inequality.

The first if() statement is not necessary