Go Down

Topic: Struggling with my BMW IBUS projects. Serial.read related issues. (Read 8304 times) previous topic - next topic

ian332isport

Hi Robin,

I've tried increasing the time between transmitted messages, and this does improve things. With 1.7ms gaps, it works most of the time, but does drop some messages. Increasing to 1.8ms seems to fix the problem, but we're the wrong side of this apparent BMW minimum time of 1.7ms.

Going back to basics, I've added the sen/sta gap detect code to your original timer based gap detect programme to see how that performs. I don't know if you recall, but you added a processData() routine that just had a delay(7) to simulate some code running. With the delay set at 7, it seems to reliably detect messages. If I increase this to 10 to simulate extra processing time, it starts to incorrectly detect gaps. If I drop it back to 9 it seems OK. Performing the same tests with the timer based gap detection, I get the same results.

I still don't understand why the two different gap detection methods behave differently when running the 'real world' processing code, but it does appear that processing time needs to be carefully managed.

That said, I just had a look over my processing code to see if there's anything I can do to make it a bit faster and I spotted a redundant FOR loop that was part of the debug routine that printed out the messages to the serial monitor. I'd commented out all the mySerial.print lines, but left the FOR loop running. Unfortunately this doesn't appear to have made any difference.

I don't really know which way to go from here. On the one hand, your timer code appears to work reliably and just needs the collision detection timer working out. On the other, the sen/sta based gap detection seems like a nice solution, but for currently unknown reasons doesn't seem reliable with the 1.7ms message gaps.

If I get a chance tomorrow evening, I'll see if I can hook it up to the car and give it a proper test with real messages. I am currently hitting it with an absolutely worst case set of data that will probably never exist on the car. I just need to get it as robust as possible.

Ian.

Robin2

Hi Ian,

I am confused by this
Quote
I've tried increasing the time between transmitted messages, and this does improve things


If you mean the messages that you transmit onto the bus what does it matter what interval there is so long as they are successfully sent.

If you mean "received" from the bus (i.e. transmitted by other devices) why not say so :).

I don't now recall what processing you need to do between messages. Perhaps it would be worth re-posting that?

...R

ian332isport


I am confused by this
Quote
I've tried increasing the time between transmitted messages, and this does improve things


It was in response to this

"what about slightly longer gaps?"


Quote
If you mean the messages that you transmit onto the bus what does it matter what interval there is so long as they are successfully sent.


It matters because of this apparent minimum 1.7ms gap imposed by BMW. Assuming a 1.7ms gap, and a 1.5ms timer to detect that gap, you potentially only have 200us to read, process and act on the message you just detected, before the next one arrives. If the transmitted messages have gaps less than 1.7ms it's not a valid test for the receiving code. I was originally sending messages from the UNO with 1.7ms gaps, and thought your message above was suggesting this was increased to see if the receiving code functioned more reliably with bigger gaps. It did  :)

Quote
If you mean "received" from the bus (i.e. transmitted by other devices) why not say so :).


Fair comment. You got me there. It was late though  :smiley-red:

Quote
I don't now recall what processing you need to do between messages. Perhaps it would be worth re-posting that?


This is the main processing code. The different EventID's are used with a switch function to run various different routines (main ones posted at the bottom) depending on the message received. All the memcmp statement are just comparing the received message with a list of messages stored in byte arrays like these.

byte DSP_STATUS_REPLY [6] = { 0x6A , 0x04 , 0xFF , 0x02 , 0x00 , 0x93 }; // DSP status reply
byte DSP_STATUS_REPLY_RST [6] = { 0x6A , 0x04 , 0xFF , 0x02 , 0x01 , 0x92 }; // DSP status ready after reset
byte DSP_VOL_UP_1 [6] = { 0x68 , 0x04 , 0x6A , 0x32, 0x11 , 0x25 }; // Rotary Volume Up 1 step
byte DSP_VOL_UP_2 [6] = { 0x68 , 0x04 , 0x6A , 0x32, 0x21 , 0x15 }; // Rotary Volume Up 2 step


For the most part, it's just a way to control the volume using messages from the IBUS. For this to work, I need to trick the car into thinking it has a DSP amplifier attached. Without the correct status messages transmitted from the Arduino back onto the bus, it will stop sending volume commands. Once the software is reliably sending and receiving messages, it's also going to do a bit of audio source switching for the radio and telephone system, but nothing particularly intensive.

Code: [Select]
void processData()
{
  length = ibusByte[1];
  if (length <= 0x02 || length >= 0x26) { // make sure length is valid
    mySerial.println("Length Invalid");
    return;
  }
  byte checksumByte = 0;
  for (int i = 0; i <= length; i++){
    checksumByte ^= ibusByte[i]; // calculate checksum from received bytes
  }
  if (ibusByte[length + 1] == checksumByte){ // see if checksum matches received checksum
    goodPacket = true;
    //for(int i = 0; i <= length + 1; i++)
    {
      //mySerial.print(ibusByte[i], HEX);
      //mySerial.print(" ");
    }
    //mySerial.println();
    comparePacket();
  }
  else { // this is just for debug
    mySerial.println("Checksum Bad");
    for(int i = 0; i <= length + 1; i++)
    {
      mySerial.print(ibusByte[i], HEX);
      mySerial.print(" ");
    }
    mySerial.println();
    goodPacket = false;
   
  }
  ibusLoopTime = currentTime1;  // Restart sleep timer
}

void comparePacket() {


  if(goodPacket == true)
  {
    EventID = 0;
    if(memcmp(ibusByte, KEY_IN, 7) == 0 ) {
      EventID = 1;
    } 
    if(memcmp(ibusByte, KEY_OUT, 7) == 0 ){
      EventID = 2;
    }
    if(memcmp(ibusByte, MFL_VOL_UP, 6) == 0 && sourceOff == false ){
      volInc = 3;
      EventID = 3;
    }
    if(memcmp(ibusByte, MFL_VOL_DOWN, 6) == 0 && sourceOff == false ){
      volInc = 3;
      EventID =4;
    }
    if(memcmp(ibusByte, DSP_VOL_UP_1, 6) == 0 ){
      volInc = 3;
      EventID = 3;
    }
    if(memcmp(ibusByte, DSP_VOL_UP_2, 6) == 0 ){
      volInc = 6;
      EventID = 3;
    }
    if(memcmp(ibusByte, DSP_VOL_UP_3, 6) == 0 ){
      volInc = 9;
      EventID = 3;
    }
    if(memcmp(ibusByte, DSP_VOL_DOWN_1, 6) == 0 ){
      volInc = 3;
      EventID = 4;
    }
    if(memcmp(ibusByte, DSP_VOL_DOWN_2, 6) == 0 ){
      volInc = 6;
      EventID = 4;
    }
    if(memcmp(ibusByte, DSP_VOL_DOWN_3, 6) == 0 ){
      volInc = 9;
      EventID = 4;
    }
    if(memcmp(ibusByte, CD_STOP, 7) == 0 ){
      EventID = 5;
    }
    if(memcmp(ibusByte, CD_PLAY, 7) == 0 ){
      EventID = 6;
    }
    if(memcmp(ibusByte, INCOMING_CALL, 6) == 0 ) {
      EventID = 7;
    }
    if(memcmp(ibusByte, PHONE_ON, 6) == 0 ) {
      EventID = 8;
    }
    if(memcmp(ibusByte, HANDSFREE_PHONE_ON, 6) == 0 ) {
      EventID = 9;
    }
    if(memcmp(ibusByte, ACTIVE_CALL, 6) == 0 ) {
      EventID = 12;
    }
    if(memcmp(ibusByte, DSP_STATUS_REQUEST, 5) == 0 ) {
      EventID = 13;
    }
    if(memcmp(ibusByte, DSP_STATUS_REPLY, 6) == 0 ) {
      EventID = 14;
    }
    if(memcmp(ibusByte, DSP_FUNC_1, 6) == 0 ) {
      EventID = 15;
    }
    if(memcmp(ibusByte, DSP_SRCE_OFF, 6) == 0 ) {
      EventID = 16;
    }
    if(memcmp(ibusByte, DSP_SRCE_CD, 6) == 0 ) {
      EventID = 17;
    }
    if(memcmp(ibusByte, DSP_SRCE_TUNER, 6) == 0 ) {
      EventID = 18;
    }
    ibusEvent = 1;
  }
  else
  {
    ibusEvent = 0;
  }
}


Code: [Select]
// Send volume data to PGA2311.
void sendVolume() {
  digitalWrite(CS_PIN, LOW);
  SPI.transfer(leftVol);
  SPI.transfer(rightVol);
  digitalWrite(CS_PIN, HIGH);
  updateLCD();
}
// End of send volume


Code: [Select]
void volUp() {
  if (leftVol < volMax && leftVol + volInc >= volMax) leftVol = volMax;
  else if (leftVol < volMax && leftVol + volInc < volMax ) leftVol += volInc;
  leftVoldB = (31.5 - (0.5 * (255 - leftVol)));
  if (rightVol < volMax && rightVol + volInc >= volMax) rightVol = volMax;
  else if(rightVol < volMax && rightVol + volInc < volMax ) rightVol += volInc;
  rightVoldB = (31.5 - (0.5 * (255 - rightVol)));
}

void volDown() {
  if (leftVol > volMin && leftVol - volInc <= volMin) leftVol = volMin;
  else if (leftVol > volMin && leftVol - volInc > volMin ) leftVol -= volInc;
  leftVoldB = (31.5 - (0.5 * (255 - leftVol)));
  if (rightVol > volMin && rightVol - volInc <= volMin) rightVol = volMin;
  else if (rightVol > volMin && rightVol + volInc > volMin ) rightVol -= volInc;
  rightVoldB = (31.5 - (0.5 * (255 - rightVol)));
}

ian332isport

Hi Robin,

I don't suppose you can explain this line from your gap timer ?

Code: [Select]
OCR2A = TCNT2 + gapTimerCount; // sets up Timer2A to run for the sample period

From the Atmel datasheet, I understand OCR2A is compared against TCNT2 (the counter value) and can trigger an interrupt when they match. What I don't understand is the ' = TCNT2 + gapTimerCount '.

gapTimerCount is obvious, but I'm struggling to understand what you're doing with TCNT2 in this context.

Thanks,

Ian.

Robin2

#139
Jun 18, 2014, 09:55 am Last Edit: Jun 18, 2014, 09:57 am by Robin2 Reason: 1
The gapTimerCount is easier to explain than a response to your earlier post.

I'm using TCNT2 like millis() is used - allowing it to wrap around when the count overflows. Calculating  TCNT2 + gapTimerCount  gives the value that TCNT2 will have after the interval even if it has wrapped around in the meantime.

I will try to look at your earlier post later - but may not get to it today.

One thought, especially if you are only interested in a few message types, is to check the message type first (without bothering with length or checksum) and only do those tests for messages that you are interested in. I'm not sure whether that would save much time overall.

I'm still uncertain about your 1.7ms logic. I don't say you are wrong, but neither am I convinced it is correct.

...R

P1X3

Hey,

I stumbled upon this thread last night, and figured I would join in.

My attempt at reading messages from bus has worked on mega board, and I have yet to see the issues with it. Simple brute force algorithm to find first valid message, and then assume sync is setup. If checksum for one of the messages is invalid, assume the arduino is out of sync and run re-sync algorithm. In my tests, out-of-sync issue happened on rare occasions only.

It was suggested to me to timestamp interrupt calls, and look for those that are longer than length of synch break and synch delimiter (21 bits). HardwareSerial_private.cpp implements HardwareSerial::_rx_complete_irq(void), what is commented as "Actual interrupt handlers." Would this be correct place to timestamp the interrupt times?

Robin2


Simple brute force algorithm to find first valid message, and then assume sync is setup.


We abandoned brute force a long time ago. But you will have to read this Thread to figure out how. I have no intention of repeating it.

...R

Hi, apologies if haven't read all of posts, and may not be addressing your current issue, just thought I'd add something - I have some very reliable logic for programatically reading iBus - I will dig it out and post the logic (code is currently VB, but I only received my first Arduino Uno today, so haven't converted yet!).  Originally used with the Resler iBus interface.  I will be using Arduino to control iBus and it will work.

Essentially, I have a loop that reads all queued incoming bytes and adds them to a stack. 

Then, I have another loop that tries to read a whole frame from that byte stack.  There are 3 possible outcomes from that attempt:

1. valid frame found --> remove frames bytes from byte stack and process frame
2. less bytes in stack than frame minimum (5) or less than the 'length' byte indicates --> do nothing
3. invalid checksum --> ONLY throw away 1 (oldest) byte from stack. 

That's it.  Works 100%.

I will post more details as I get my code converted, I need to get my physical layer device sorted, not sure what that will be yet.  Am assuming native Arduino with some 5v/12v converting won't be enough (e.g. collision handling), but don't know.

Now I will go read rest of your posts.


Regards,

HobbyTech.

Robin2

Hi, apologies if haven't read all of posts, and may not be addressing your current issue,
I'm not aware that anyone has a current issue. The last post before yours was June 25th

...R

Go Up