I think that your problem is that you assume that, when you have received one byte, you have received all bytes.
That is not the case. A byte might still be in transmission from the sender and your while() will end. Next time that your reading starts, you start with that last byte that by now is received.
You should read till your byte count is 48 and next process the data. Although Robin's updated Serial Input Basics is text based, example 3 and example 5 demonstrate how to receive till you have a full packet.
A 48 byte packet @500 kBaud takes roughly 1 millisecond to be transmitted. If I understand you correctly, you want to send every 10 ms. You will easily be able to add a timeout mechanism to your receiver.
Below program demonstrates the basics to give you the idea; it might need some fine tuning. Note that it uses Serial3 on the Mega so you need to replace Serial3 by Serial2 for your use case.
// status of receive operation
enum RXSTATUS
{
IDLE,
INPROGRESS,
COMPLETE,
TIMEOUT,
};
// buffer to store received data
uint8_t rxBuffer[48];
// receive timeout value
const uint32_t rxTimeout = 20;
void setup()
{
Serial.begin(115200);
Serial.println(F("Mega receiver 0.1"));
Serial3.begin(500000);
}
void loop()
{
// status of receive
RXSTATUS status = receive();
// previous status of receive for state change
static RXSTATUS prevStatus = IDLE;
// only display changes in receive status
if (status != prevStatus)
{
Serial.print(F("Status changed from "));
Serial.print(prevStatus);
Serial.print(F(" to "));
Serial.println(status);
prevStatus = status;
}
// if a comnplete packet is received
if (status == COMPLETE)
{
Serial.print(sizeof(rxBuffer));
Serial.println(F(" bytes received"));
for (uint8_t cnt = 0; cnt < sizeof(rxBuffer); cnt++)
{
if (rxBuffer[cnt] <= 0x10)
Serial.print(F("0"));
Serial.print(rxBuffer[cnt], HEX);
Serial.print(F(" "));
}
Serial.println();
}
}
/*
Receive on Serial3
Returns:
status
*/
RXSTATUS receive()
{
// status of receive
static RXSTATUS rxStatus = IDLE;
// index in receive buffer
static uint8_t index;
// time when last byte was received
static uint32_t rxTime;
// if receive in progress
if (rxStatus == INPROGRESS)
{
// check for timeout
if (millis() - rxTime > rxTimeout)
{
// reset index
index = 0;
// update status to idle
rxStatus = IDLE;
// indicate to caller that receive has timed out
return TIMEOUT;
}
}
while (Serial3.available())
{
// time of last byte rceived
rxTime = millis();
rxBuffer[index++] = Serial3.read();
// change status to INPROGRESS
rxStatus = INPROGRESS;
// if full packet received
if (index == sizeof(rxBuffer))
{
// reset index
index = 0;
// change status to COMPLETE
rxStatus = IDLE;
// indicate to caller that receive is complete
return COMPLETE;
}
}
// return the status
return rxStatus;
}
I did write an elementary sender that can force a time out by setting an inter character delay.
Typical output if a full packet is received
09:13:51.921 -> Status changed from 0 to 1
09:13:51.921 -> Status changed from 1 to 2
09:13:51.921 -> 48 bytes received
09:13:51.962 -> 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 010 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
Typical output with a timeout (icd = 25 ms)
09:15:00.613 -> Status changed from 0 to 1
09:15:00.644 -> Status changed from 1 to 3
09:15:00.644 -> Status changed from 3 to 0
// Edit
My sender only sends every second; this was done so the receiving Mega has time to print; I actually should have tried 2000000 baud rate for Serial on the Mega.
But I'm 99.99% confident that this will work correctly with limited printing.