Good morning.
This is my first attempt at any sort of software coding, so please try and look past any poor/strange uses of the code. I'll get better with time - honest. Also note that my code is currently full of loads of mySerial.print and LCD.print commands. These are mostly for debug and testing. Most will be removed at some point.
My project is essentially an Arduino interface between the IBUS (data bus used to control the audio system and other non critical systems on older BMW cars) and an SPI controlled volume control circuit (based around the TI PGA2311 volume control IC). It listens for messages on the IBUS and adjusts the volume, responds to certain buttons and will eventually do a bit of audio source switching.
For the most part it actually works quite well, but falls over when the car transmits a long IBUS message. I don't actually need to respond to or act on any IBUS messages longer than 7 HEX bytes, but the car sends messages around the bus that can be over 20 HEX bytes. My code (below) currently works fine with 6 byte messages.
If I receive messages less than 6 bytes it ignores them because of the:
if (Serial.available()>=6)
If I receive messages longer than 6 bytes, it tries to read them before they have arrived and ends up missing them. This causes the LENGTH byte to read the wrong value and the code tries to read in serial that doesn't exist. My chkSUM routine then fails to calculate.
This is what I get if I receive a 7 byte message (44 05 BF 74 04 00 8E)
IBUSbyte = 44 5 BF 74 4 0 FF
Source= 44
Length= 5
IBUS checksum = 8E
IBUS checksum bad
It's failed to read the final byte, but my chkSUM routine has correctly calculated the checksum. It has also correctly identified the message as corrupt because the calculated chkSUM doesn't match the received chkSUM.
If I receive the same message again, I get the following:
IBUSbyte = 8E 44 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 AF AF F8 FF F8 FF 1 0 1 0 0 0 0 0 2 0 0 2 3 B6 2 0 0 27 8 4 2 4 14 4 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Source= 8E
Length= 44
IBUS checksum = 0
IBUS checksum matches
No Match
IBUSbyte = 5 BF 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 AF AF F8 FF F8 FF 1 0 1 0 0 0 0 0 2 0 0 2 3 B6 2 0 0 27 8 4 2 4 14 4 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 27 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 A6 2 0 0 E8 3 0 0 0 0 0 0 0 4E 1 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Source= 5
Length= BF
IBUS checksum = 0
IBUS checksum matches
No Match
From this, you can see that the 7th byte (8E) it failed to read the first time, has come in as the source byte and the length byte (44) is what should have been the source byte. It goes down hill rapidly from there and the Arduino has to be reset to recover from this.
My main problem is that the IBUS messages don't have any start or end characters for me to detect. I need to read the first two bytes before I know the length of the message. I can then read in the rest of the message and process it.
For reference, this is the anatomy of an IBUS message.
IBUS protocol:
Example:
When key is inserted into the ignition lock these messages are sent.
44 05 BF 74 04 00 8F // in - key detected
44 05 BF 74 00 FF 75 // out - key removed
Structure of an IBUS packet (Ref first IBUS packet above):
1st byte is the Source ID - 0x44 - in this case the car immobilser module
2nd byte is length of the packet excluding the first two bytes (source & length) - 0x05
3rd byte is the destination ID - 0xBF - in this case the light control module
4th byte onwards is the actual data - 0x74, 0x00, 0xFF
Last byte is the checksum - 0x8F
Checksum is generated by XOR of the entire packet excluding the checksum itself.
HEX - Binary
44 = 01000100
05 = 00000101
BF = 10111111
74 = 01110100
04 = 00000100
00 = 00000000
XOR = 10001111 = 8F
This is the code that currently works fine with 6 byte messages:
void serialEvent() {
if (Serial.available()>=6) { // there are bytes in the serial buffer to read
IBUSbyte[0] = Serial.read(); // read in source byte.
IBUSbyte[1] = Serial.read(); // read in length byte.
LENGTH = IBUSbyte[1];
if (IBUSbyte[0] == 0x50 || IBUSbyte[0] == 0x18 || IBUSbyte[0] == 0x68 || IBUSbyte[0] == 0xD7 || IBUSbyte[0] == 0x44 || IBUSbyte[0] == 0xC8 || IBUSbyte[0] == 0x80 && LENGTH <=7){ // ignore messages from some modules
for (int i = 2; i <= LENGTH+2; i++) {// read in rest of message.
IBUSbyte[i] = Serial.read();
}
chkSUM(); // go off and see if checksum matches
}
else {
debug();
memset(IBUSbyte,0,sizeof(IBUSbyte));
mySerial.println("No Match ");
}
}
} // End of IBUS read routine.
I've spent more hours than I care to remember trying to improve on this, but my tiny brain just can't figure it out. If anyone can suggest any improvements or a better way to deal with this, I'd appreciate it.
I was going to post the full code, but it made the message longer than allowed.
Here's my chkSUM code:
void chkSUM() {
checksumBYTE = 0;
byteSTATE = 0;
for (int i = 0; i < LENGTH+1; i++){
checksumBYTE ^= IBUSbyte[i];}
if (IBUSbyte[LENGTH + 1] == checksumBYTE){
byteSTATE = 1;}
else if (IBUSbyte[LENGTH + 1] != checksumBYTE){
byteSTATE = 0;}
if (byteSTATE == 1) {
compare();
}
else if (byteSTATE == 0) {
debug();
checksumBYTE = 0;
memset(IBUSbyte,0,sizeof(IBUSbyte));
}
}
Many thanks,
Ian.