Reading serial data in wrong order

Hi, i receive data from serial but it is only correct when i power the arduino before my engine ecu with can bus.
Correct data is: 0x0 0x0 0x6 0x1 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8
When aruino is powered after engine ecu i get something like: 0x3 0x4 0x5 0x6 0x7 0x8 0x0 0x0 0x6 0x1 0x1 0x2.
Begin of my can message is always 0x0 0x0 0x6.
How can i change this code to find the beginning?

void recv(unsigned long *id, uchar *buf){ if(canSerial.available()) { 
unsigned long timer_s = millis();
 int len = 0; 
uchar dta[20]; 
while(1) { 
while(canSerial.available()) { 
dta[len++] = canSerial.read();
 if(len == 12) break; 	
if((millis()-timer_s) > 10) {
 return 0; // Reading 12 bytes should be faster than 10ms, abort if it takes longer, we loose the partial message in this case
 } 
}
 if(len == 12) // Just to be sure, must be 12 here 
{ 
unsigned long __id = 0;
 for(int i=0; i<4; i++) // Store the id of the sender 
{ 
__id <<= 8; 
__id += dta[i]; 
} 
*id = __id;
 for(int i=0; i<8; i++) // Store the message in the buffer
 { 
buf[i] = dta[i+4];
 }
 return 1;
 } 
if((millis()-timer_s) > 10) {
  return 0; // Reading 12 bytes should be faster than 10ms, abort if it takes longer, we loose the partial message in this case
 } 
} 
}
 return 0;
}

Implement a finite state machine to detect the three header bytes and read the remainder of the packet.

Dont understand what you mean.
Why doesnt it work when i add;

If(len == 3 && dta[2] != 0x6){
len = 0;
}

I thought this would work, but it doesnt

You misspelled "if"?

Was correct in my code, but doesnt work for some reason

you need to constantly check for the start sequence and reset you input index whenever there's not a match

Hello brianden
I guess the sketch has mislaligned the start point of message.
Have a nice day and enjoy coding in C++.

How would you do that? I tried with adding

if(len == 3 && dta[2] != 0x6)
{ 
len = 0; 
}

But didnt work

as you read each byte, you check the first few bytes for the start sequence, resetting an index to copy the data into a buff if the sequence is not followed

look at this Automess code starting at lines 63

Hello

This may help : 43uw2o - Online C++ Compiler & Debugging Tool - Ideone.com

( this was an answer for this topic : Find an known hexa sentence on a bigger hexa sentence )

this is for communication, not debugging

the conventional communication problem is code starts monitoring a byte stream in the middle of transmission or after corruption and needs to resynchronize to detect the start of a message.

so each message has a sequence of bytes that represents the start of message. it may also have a checksum and it may have escape sequences if those special start characters are actually in the data.

you could just collect a bunch of data and search for the start in that data, but of the byte stream is continuous, additional bytes may need to be appended.

the more common approach is to search for the start sequence as the bytes are received and use a valid sequence to begin collecting the message. the size of the message may also be in the message header or the message type implies the message length.

the message is processed after the appropriate # of bytes representing a complete message

You'll have to show your revised code :wink:

One thing is to check the first 3 bytes;

uchar header[] = {0x00, 0x00, 0x06};
if (len == 3 && memcmp(dta, header, sizeof(header) != 0)
{
  len = 0;
}

Your function returns 0 at several places but your function is expected to return a void; the compiler will bark at you but not bite you. In file -> preferences in the IDE, set the warning level to ALL to see it.

Correct data is: 0x0 0x0 0x6 0x1 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8

Transmit Sketch:

byte myData[] = 
{0x00, 0x00, 0x06, 0x01, 0x01, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8};

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

void loop() 
{
  
  Serial.println("Sending data.");
  Serial3.write(myData,sizeof(myData));
  delay(1000); 
}

Receiver Sketch:

#include<SoftwareSerial.h>
SoftwareSerial SUART (2, 3);  //SRX = 2, STX = 3
byte myData[9];

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

void loop()
{
  byte n = SUART.available();
  if (n == 3)
  {
    byte x1 = SUART.read();
    byte x2 = SUART.read();
    byte x3 = SUART.read();
    long int x = (long) x1<<16| x2 << 8 |x3;
    if (x == 0x000006)
    {
      SUART.readBytesUntil('\n', myData, 9);
      for(int i=0; i<9; i++)
      {
        byte x = myData[i];
        if(x < 0x10)
        {
          Serial.print('0');
        }
        Serial.print(x);
      }
      Serial.println();
    }
  }
}

Receiver Output:

010102030405060708
010102030405060708
010102030405060708
010102030405060708
010102030405060708
010102030405060708
010102030405060708
010102030405060708

what if the first start byte is x2?

it would help if the first two bytes weren't the same value

There is no '\n' in the message unless it's part of the data in which case you have a problem. And as @gcjr pointed out, you're not syncing.

Here is one way to check using a finite state machine; not sure if it's the most efficient way. You can implement a different state machine (possibly with only two states, one to read the header and one to read the data).

First you need to define the steps; place below code before the first time that it is used (e.g. near the top of your code)

enum class RCVSTATES
{
  H0,   // waiting for header byte 0
  H1,   // waiting for header byte 1
  H2,   // waiting for header byte 2
  DTA,  // receive data
};

Next you can write the frame work for the state machine

/*
  receive finite state machine
  In:
    pointer to buffer; your responsibility to make sure that buffer is big enough
  Returns:
    true if a complete packet is received, else false
*/
bool recv2(uint8_t *buf)
{
  // current state of finite state machine
  static RCVSTATES state;
  // return value
  bool rv = false;

  switch (state)
  {
    case RCVSTATES::H0:
      break;
    case RCVSTATES::H1:
      break;
    case RCVSTATES::H2:
      break;
    case RCVSTATES::DTA:
      break;
  }

}

And fill in the logic in the states. Below adds some additional variables. Note that this is non-blocking code so has to be called repeatedly; I've used Serial instead of canSerial.

/*
  receive finite state machine
  In:
    pointer to buffer; your responsibility to make sure that buffer is big enough
  Returns:
    true if a complete packet is received, else false
*/
bool recv2(uint8_t *buf)
{
  // current state of finite state machine
  static RCVSTATES state;
  // return value
  bool rv = false;

  // index where to store in buf
  static uint8_t index;
  // header that you're looking for
  uint8_t header[] = {0x00, 0x00, 0x06};
  // time that last byte was received
  static uint32_t lastTime;

  // check for timeouts
  if (state != RCVSTATES::H0 && (millis() - lastTime > 10))
  {
    index = 0;
    state = RCVSTATES::H0;
    return false;
  }

  // if nothing to read
  if (Serial.available() == 0)
  {
    return false;
  }

  // 'reset' timer
  lastTime = millis();

  switch (state)
  {
    case RCVSTATES::H0:
      buf[index] = Serial.read();
      if (buf[index] == header[index])
      {
        index++;
        state = RCVSTATES::H1;
      }
      break;
    case RCVSTATES::H1:
      buf[index] = Serial.read();
      if (buf[index] == header[index])
      {
        index++;
        state = RCVSTATES::H2;
      }
      else
      {
        index = 0;
        state = RCVSTATES::H0;
      }
      break;
    case RCVSTATES::H2:
      buf[index] = Serial.read();
      if (buf[index] == header[index])
      {
        index++;
        state = RCVSTATES::DTA;
      }
      else
      {
        index = 0;
        state = RCVSTATES::H0;
      }
      break;
    case RCVSTATES::DTA:
      if (index != 12)
      {
        buf[index++] = Serial.read();
      }
      else
      {
        rv = true;
        index = 0;
        state = RCVSTATES::H0;
      }
      break;
  }

  return rv;
}

The above code keeps the receiving part separate from the logic; you will have to implement e.g. the check for the ID outside this function. Below how it can be used

uint8_t buffer[20];

void setup()
{
  // put your setup code here, to run once:

}

void loop()
{
  if (rcv2(buffer) == true)
  {
    // complete packet received
    // process e.g. ID
  }
}

Right! It is a binary transmission; so, there could be data byte as 0x0A ('\n'). In that case, it is a problem. Therefore, the OP could be advised to adopt ASCII Transmission scheme (Fig-1) with '\n' as the terminating character. In the given exaample of the OP, there is no 0x0A in the data stream. The redaBytesUntil() function/method will terminate when 9 bytes data are received.

My attempt was just to show the OP that a rough solution could be deviesd and then tune it based on the feed back of the Forum to arrive at a robust and reliable solution.

char myData[] = "006112345678";
Figure-1:

Transmit Sketch:

char myData[] = "006112345678";

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

void loop() 
{
  Serial.println("Sending data.");
  Serial3.write(myData,sizeof(myData));
  Serial.print('\n');
  delay(1000); 
}

Receive Sketch:

#include<SoftwareSerial.h>
SoftwareSerial SUART (2, 3);  //SRX = 2, STX = 3
char myData[9];

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

void loop()
{
  byte n = SUART.available();
  if (n == 3)
  {
    char myArray[3] = "";
    for(int i=0; i<3; i++)
    {
      myArray[i] = SUART.read();
    }
    unsigned long x = strtoul(myArray, NULL, 16);
    if (x == 0x000006)
    {
      SUART.readBytesUntil('\n', myData, 9);
      for(int i=0; i<9; i++)
      {
       Serial.print(myData[i]);
      }
      Serial.println();
    }
  }
}

Receiver Output:

112345678
112345678
112345678

The above condition will not be full-filled.

After few trials, the system will be synchronized to the preamble 0x000006 and will remain locked for natural reason.

there's no reason it shouldn't synchronize as soon as the start sequence occurs. it should synchronize on 0x00 0x00 0x00 0x06

this is a poorly chosen start sequence, it would be better of any # of 0x00s could be present (i.e. preamble) and can be ignored before recognizing the start byte

You're lucky. strtoul() works on character arrays terminated with '\0'; your myArray is not terminated.

1 Like