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;
}
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
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
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.
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.
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