Help with checksum calculation

Im doing a prototype with an UNO and RS232 Shiled (Max232 shield). Hooked it up to an aftemarket car ECU and i could successfully read the HEX data from the ECU and print in CoolTerm, so hardware i'm all good.

But im not really sure on how to address my current issue.

The ECU sends out data according to the following protocol:

A packet is sent out every 10ms, each packet consists of 5 byte.

1 byte = Channel Number
2 byte = Is always A3 (163)
3 & 4 byte = Is the channel value
5 byte = Checksum, preceding sum of the 4 bytes.

15 different channels are specified in the documentation. I did some checks on the data i got from from the ECU, and all 15 channels are not sent out every time. Channel 1 (RPM) repeats most frequently (every 7-8 loop) while some other channels repeats less frequently.

A typical packet would look something like this:

11 A3 04 BA 72

To calculate the sum of the 1-4 bytes it would look this:

11 + A3 + 04 + BA = 172

But the first digit in the result would be dropped since it can hold one byte. So 72 then, which matches the 5 byte in the example.

The value of the channel would be:

04BA, converted to dec: 1210 . This is the Water temperature, but it will require another calculation, specified for each channel. For water: 1210/10-100=21

Back to my actual problem then. Im not really sure on how to identify my 5 byte packet. I guess it would be easier if each packet would start with A3, instead being the second byte in the packet. I guess i would need to look for A3 and its preceding 1 byte and then byte 3,4,5. do the checksum calculation, then calculate the decimal value of byte 3,4. Then finally calculate the actual value based on the documentation and store the value in a pre-defined variable (wTemp), based on the channel number of the packet.

Any ideas on how?

As the second byte is always A3 you need to keep the previous until you read A3.

byte b[5];

b[0] = readByte();
b[1] = readByte();
while (b[1] != 0xA3)
{
  b[0] = b[1];
  b[1] = readByte();
}
b[2] = readByte();
b[3] = readByte();
b[4] = readByte();

byte crc = b[0] + b[1] + b[2] + b[3];  // automagically truncates 
if (b[4] != crc) // CRC error
{
  ... handle error
}

float temp = (b[2] * 256 + b[3] ) /10.0 - 100;  // or can it only be integer?
int channel = b[0];

Thank your for input, channel value is always an integer

I’ll have a look at your example code and i’ll try to implement it in my sketch below

#include <SoftwareSerial.h>
 
SoftwareSerial mySerial(6, 7); // RX, TX
 
void setup()
{
    // Open serial communications and wait for port to open:
    Serial.begin(19200);
    while (!Serial) {
        ; // wait for serial port to connect. Needed for Leonardo only
    }
 
    // set the data rate for the SoftwareSerial port
    mySerial.begin(19200);

}
 
void loop() // run over and over
{
    //Skickar från ECU till Arduino
    if (mySerial.available())
    Serial.write(mySerial.read());
}

did some tests today but had problem incorporating this in my sketch. Any chance that anyone could help me combining my sketch with the example from Rob, would be greatly appreciated

/M

If the packets are spaced 10ms apart, I think maybe you could use the timing to identify the end of one packet / beginning of the next. But I’m not exactly sure how that would work in code; I don’t really know a whole lot about the Arduino libraries.

Anyway, I don’t recommend you use millis(): it’s too jittery. Use micros() instead.

big_mike:
did some tests today but had problem incorporating this in my sketch. Any chance that anyone could help me combining my sketch with the example from Rob, would be greatly appreciated

/M

Can you post your latest sketch which includes a serious try?

Yes of course, been altering it a bit all night, changed some names etc.

Got it to compile at least, but does not output anything. Looks like it halts before serial available. I’ll speak to programmers tomorrow at work as well.

#include <SoftwareSerial.h>
 
SoftwareSerial mySerial(6, 7); // RX, TX
byte b[5];


 
void setup()
{
    // Open serial communications and wait for port to open:
    Serial.begin(19200);
    while (!Serial) {
        ; // wait for serial port to connect. Needed for Leonardo only
    }
 
    // set the data rate for the SoftwareSerial port
    mySerial.begin(19200);

}
   
void loop() // run over and over
{
    //Skickar från ECU till Arduino
    
    
    
    //Serial.write(""); //Test to see where it halts
    if (mySerial.available())
     //Serial.write("Test to see where it halts"); 
      int incomingByte = 0;
     incomingByte = mySerial.read();
     //Serial.write(mySerial.read());
     
     

    
b[0] = incomingByte;
b[1] = incomingByte;
while (b[1] != 0xA3)
{
  b[0] = b[1];
  b[1] = incomingByte;
}
b[2] = incomingByte;
b[3] = incomingByte;
b[4] = incomingByte;

byte crc = b[0] + b[1] + b[2] + b[3];  // automagically truncates 
if (b[4] != crc) // CRC error
{
  serial.write("Error");
}

float temp = (b[2] * 256 + b[3] ) /10.0 - 100;  // or can it only be integer?
int channel = b[0];

    Serial.write(temp);
  
  
  }

big_mike:
Got it to compile at least, but does not output anything. Looks like it halts before serial available. I’ll speak to programmers tomorrow at work as well.

I don’t have your hardware for testing.
But I tried a different approach.

This code is trying to find “sending pause” in the data from mySerial, and if a timeout of 2 milliseconds (2000µs) is found, the byte count is reset. Then if 5 bytes sent without a timeout will be taken as data input.

Input is sent to Serial for debugging reasons, then I try to check the checksum.

Perhaps you can understand what this is trying to do (untested code):

#include <SoftwareSerial.h>
SoftwareSerial mySerial(6, 7); // RX, TX

byte packet[5];
#define PACKETSIZE (sizeof(packet))

boolean receivePacket()
{
  static unsigned long lastByteReceived;
  static byte count; 
  long now=micros();
  if (now-lastByteReceived >=2000) count=0;  // timeout, restart byte count
  if (!mySerial.available()) return false;
  lastByteReceived=now;
  if (count<PACKETSIZE)
  {
    packet[count]= mySerial.read();
    count++;
  }
  if (count>=PACKETSIZE)  
  {
    count=0;
    return true;
  }
  return false;
}

boolean checksumOK()
{
  byte sum=0;
  for (int i=0;i<PACKETSIZE-1;i++) sum+= packet[i];
  if (sum==packet[4]) return true;
  else return false;
}

void setup()
{
  Serial.begin(115200);
  while (!Serial) {
     ; // wait for serial port to connect. Needed for Leonardo only
  }
  // set the data rate for the SoftwareSerial port
  mySerial.begin(19200);  
  Serial.println("Starting program...");
}

void loop() {
  if (receivePacket())
  {
    for (int i=0;i<PACKETSIZE;i++) 
    {
      Serial.print(packet[i],HEX);
      Serial.print('\t');
    }
    if (checksumOK()) Serial.println("OK");
    else Serial.println("FAIL");
  }
}

As the debugging characters sent to Serial are much more than 5 bytes per packet, the debug speed for Serial must be much faster than 19200 baud to work flawlessly. I’ve set Serial to 115200 therefore.

boolean checksumOK()
{
  byte sum=0;
  for (int i=0;i<PACKETSIZE-1;i++) sum+= packet[i];
  if (sum==packet[4]) return true;
  else return false;
}

If you’re going to use named constants, you might as well be consistent.

boolean checksumOK()
{
  byte sum=0;
  for (int i=0;i<PACKETSIZE-1;i++) sum+= packet[i];
  if (sum==packet[PACKETSIZE-1]) return true;
  else return false;
}

or even

boolean checksumOK()
{
  byte sum = 0;
  for (byte i = 0; i < PACKETSIZE - 1; ++i) sum += packet[i];
  return sum == packet[PACKETSIZE - 1];
}

Thanks for the input. I used the code from Jurs and combined it with the parts from Rob for calculating channel value.

All works good, but i had not the chance to hook it up to the ECU yet. Have just tried to send a 5 byte hex packet from the Coolterm to the Arduino and print on my second computer.

Works good and calculates the channel data correctly

I’ll do some actual tests with the ECU later this week.

Thanks everyone!