Thursday, September 27, 2012

2nd update for today

New code; this time I used the Saab Park Assist (SPA) message to display "BlueSaab" on the SID. It seems to work pretty well; it takes a few seconds for the display to register, but the music lines are on immediately so you'll see "CD1 PLAY" for about 5 seconds, and then "BlueSaab" appears. All other SID functions seem to work properly, and the turn signals seem to work w/out interruption ;)

Going back to Radio mode, however, seems to be intermittent. This has always kind of been an issue; if the Arduino doesn't see the Radio selection, it doesn't know to switch back to Radio mode (aka, turn off the CD Changer code/SID display). The SID will go back to Radio mode, and you'll hear the station you're on, but if the Arduino didn't see the switch, "BlueSaab" will pop up again (that 5 second) delay. I just cycle back to the CDC input and back to the radio (3 presses of the SRC button on the wheel) and it seems to clear it up (maybe after a few cycles).

Stupid bugs...


// ----------------------------------------------
// SECUDUINO
// http://secuduino.blogspot.com/
// By Igor Real
// 16/05/2011
//
// Saab CDC Changer Emulator
// Seth Evans
// 27 Sep 2012
// ----------------------------------------------

#include <CAN.h>

int cdbutton;

void setup() {
  // set up CAN
  CAN.begin(47);  // Saab I-Bus is 47.619kbps
  Serial.begin(115200);
  cdbutton = 0;
  CAN_TxMsg.header.rtr=0;     // this value never changes
  CAN_TxMsg.header.length=8;  // this value never changes
}

void loop() {
  if (CAN.CheckNew()){
    CAN_TxMsg.data[0]++;
    CAN.ReadFromDevice(&CAN_RxMsg);
    //PrintBus();
    if (CAN_RxMsg.id==0x3C0){
      CDC();
      if (CAN_RxMsg.data[1]==0x24){
        cdbutton = 1;
        Serial.println("CDC");
      }
      else if (CAN_RxMsg.data[0]==0x80 && CAN_RxMsg.data[1]==0x14){
        cdbutton = 0;  
        Serial.println("Radio");
      }
    }

    if (cdbutton==1){
      CDC();
      delay(5);
      iPodOn();
    }
    else {
      //iPodOff();
    }
  }
}

void CDC(){
  CAN_TxMsg.id=0x3C8;     // CD Changer
  CAN_TxMsg.data[0]=0xE0;
  CAN_TxMsg.data[1]=0x00;
  CAN_TxMsg.data[2]=0x3F;
  CAN_TxMsg.data[3]=0x31; // playing disc 1
  CAN_TxMsg.data[4]=0xFF;
  CAN_TxMsg.data[5]=0xFF;
  CAN_TxMsg.data[6]=0xFF;
  CAN_TxMsg.data[7]=0xD0; // married "OK" code
  CAN.send(&CAN_TxMsg);
}

void iPodOn(){
  CAN_TxMsg.id=0x357;     // enable the SID (SPA) text
  CAN_TxMsg.data[0]=0x1F;
  CAN_TxMsg.data[1]=0x02;
  CAN_TxMsg.data[2]=0x05;
  CAN_TxMsg.data[3]=0x12;
  CAN_TxMsg.data[4]=0x00;
  CAN_TxMsg.data[5]=0x00;
  CAN_TxMsg.data[6]=0x00;
  CAN_TxMsg.data[7]=0x00;
  CAN.send(&CAN_TxMsg);
  delay(10);

  CAN_TxMsg.id=0x337;  
  CAN_TxMsg.data[0]=0x42;
  CAN_TxMsg.data[1]=0x96;
  CAN_TxMsg.data[2]=0x02;
  CAN_TxMsg.data[3]=0x42; // B
  CAN_TxMsg.data[4]=0x6C; // l
  CAN_TxMsg.data[5]=0x75; // u
  CAN_TxMsg.data[6]=0x65; // e
  CAN_TxMsg.data[7]=0x53; // S
  CAN.send(&CAN_TxMsg);
  delay(10);

  CAN_TxMsg.data[0]=0x01;
  CAN_TxMsg.data[3]=0x61; // a
  CAN_TxMsg.data[4]=0x61; // a
  CAN_TxMsg.data[5]=0x62; // b
  CAN_TxMsg.data[6]=0x20; // _
  CAN_TxMsg.data[7]=0x20; // _
  CAN.send(&CAN_TxMsg);
  delay(10);

  CAN_TxMsg.data[0]=0x00;
  CAN_TxMsg.data[3]=0x20; // _
  CAN_TxMsg.data[4]=0x20; // _
  CAN_TxMsg.data[5]=0x00; //
  CAN_TxMsg.data[6]=0x00; //
  CAN_TxMsg.data[7]=0x00; //
  CAN.send(&CAN_TxMsg);
  delay(10);
}

void iPodOff(){
  CAN_TxMsg.id=0x357;     // turn off the SID (SPA) text
  CAN_TxMsg.data[0]=0x1F;
  CAN_TxMsg.data[1]=0x00;
  CAN_TxMsg.data[2]=0x05;
  CAN_TxMsg.data[3]=0x08;
  CAN_TxMsg.data[4]=0x00;
  CAN_TxMsg.data[5]=0x00;
  CAN_TxMsg.data[6]=0x00;
  CAN_TxMsg.data[7]=0x00;
  CAN.send(&CAN_TxMsg);
  delay(10);
}

void PrintBus(){
  if (CAN_RxMsg.id==0x3C0){
    Serial.print(CAN_RxMsg.id,HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[0],HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[1],HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[2],HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[3],HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[4],HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[5],HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[6],HEX);
    Serial.print(";");
    Serial.println(CAN_RxMsg.data[7],HEX);
  }
}

Latest software

So I commented out the line calling the routine displaying text on the SID because it was causing issues with the I-Bus communications. For example, turning on either blinker, occasionally the indicators on the dash would not blink, or blink twice fast, as they time themselves with the outside blinker. The timing with the SID text display was interrupting this, and god knows what else. Of course, this is only an indication issue, the outside blinkers still work perfectly.


// ----------------------------------------------
// SECUDUINO
// http://secuduino.blogspot.com/
// By Igor Real
// 16/05/2011
//
// Saab CDC Changer Emulator, BlueSaab
// Seth Evans
// 27 Sep 2012
// ----------------------------------------------

#include <CAN.h>

int cdbutton;

void setup() {
  // set up CAN
  CAN.begin(47);  // Saab I-Bus is 47.619kbps
  Serial.begin(115200);
  cdbutton = 0;
  CAN_TxMsg.header.rtr=0;  // this value never changes
  CAN_TxMsg.header.length=8;  // this value never changes
}

void loop() {
  if (CAN.CheckNew()){
    CAN_TxMsg.data[0]++;
    CAN.ReadFromDevice(&CAN_RxMsg);
    //PrintBus();
    if (CAN_RxMsg.id==0x3C0){
      CDC();
      if (CAN_RxMsg.data[1]==0x24){
        cdbutton = 1;
        Serial.println("CDC");
      }
      else if (CAN_RxMsg.data[0]==0x80 && CAN_RxMsg.data[1]==0x14){
        cdbutton = 0;  
        Serial.println("Radio");
      }
    }

    if (cdbutton==1){
      CDC();
      delay(5);
      //iPodOn();
    }
    else {
      iPodOff();
    }
  }
}

void CDC(){
  CAN_TxMsg.id=0x3C8;  
  CAN_TxMsg.data[0]=0x80;
  CAN_TxMsg.data[1]=0x00;
  CAN_TxMsg.data[2]=0x3F;
  CAN_TxMsg.data[3]=0x41;
  CAN_TxMsg.data[4]=0x00;
  CAN_TxMsg.data[5]=0x00;
  CAN_TxMsg.data[6]=0x00;
  CAN_TxMsg.data[7]=0xD0;
  CAN.send(&CAN_TxMsg);
}

void iPodOn(){
  CAN_TxMsg.id=0x348;
  CAN_TxMsg.data[0]=0x11;
  CAN_TxMsg.data[1]=0x02;
  CAN_TxMsg.data[2]=0x04;
  CAN_TxMsg.data[3]=0x19;
  CAN_TxMsg.data[4]=0x00;
  CAN_TxMsg.data[5]=0x00;
  CAN_TxMsg.data[6]=0x00;
  CAN_TxMsg.data[7]=0x00;
  CAN.send(&CAN_TxMsg);
  delay(5);

  CAN_TxMsg.id=0x328;  
  CAN_TxMsg.data[0]=0x42;
  CAN_TxMsg.data[1]=0x96;
  CAN_TxMsg.data[2]=0x02;
  CAN_TxMsg.data[3]=0x42; // B
  CAN_TxMsg.data[4]=0x6C; // l
  CAN_TxMsg.data[5]=0x75; // u
  CAN_TxMsg.data[6]=0x65; // e
  CAN_TxMsg.data[7]=0x53; // S
  CAN.send(&CAN_TxMsg);
  delay(5);

  CAN_TxMsg.data[0]=0x01;
  CAN_TxMsg.data[3]=0x61; // a
  CAN_TxMsg.data[4]=0x61; // a
  CAN_TxMsg.data[5]=0x62; // b
  CAN_TxMsg.data[6]=0x20; // _
  CAN_TxMsg.data[7]=0x20; // _
  CAN.send(&CAN_TxMsg);
  delay(5);
}

void iPodOff(){
}

void PrintBus(){
  if (CAN_RxMsg.id==0x3C0){
    Serial.print(CAN_RxMsg.id,HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[0],HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[1],HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[2],HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[3],HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[4],HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[5],HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[6],HEX);
    Serial.print(";");
    Serial.println(CAN_RxMsg.data[7],HEX);
  }
}

Tuesday, September 25, 2012

Software update


I'm still playing with the SID display timing...this version seems to work pretty well, except the radio has a tendency to just shut off randomly...(?).

I tried using 0x337 SPA (Saab Park Assist) Messages to display the audio status, but that had it's own issues as well. As you can see below, I'm currently using 0x328 SID Audio Text Messages.

// ----------------------------------------------
// SECUDUINO
// http://secuduino.blogspot.com/
// By Igor Real
// 16/05/2011
//
// Saab CDC Changer Emulator
// Seth Evans
// 25 Sep 2012
// ----------------------------------------------

#include <CAN.h>

int cdbutton;

void setup() {
  // set up CAN
  CAN.begin(47);  // Saab I-Bus is 47.619kbps
  Serial.begin(115200);
  cdbutton = 0;
  CAN_TxMsg.header.rtr=0;  // this value never changes
  CAN_TxMsg.header.length=8;  // this value never changes
}

void loop() {
  CDC();
  delay(5);
  if (CAN.CheckNew()){
    CAN_TxMsg.data[0]++;
    CAN.ReadFromDevice(&CAN_RxMsg);
    //PrintBus();
    if (CAN_RxMsg.id==0x3C0){
      if (CAN_RxMsg.data[1]==0x24){
        cdbutton = 1;
        Serial.println("CDC");
      }
      else if (CAN_RxMsg.data[0]==0x80 && CAN_RxMsg.data[1]==0x14){
        cdbutton = 0;  
        Serial.println("Radio");
      }
    }
  }
  if (cdbutton==1){
    iPodOn();
  }
  else {
    iPodOff();
  }
}

void CDC(){
  CAN_TxMsg.id=0x3C8;  
  CAN_TxMsg.data[0]=0x80;
  CAN_TxMsg.data[1]=0x00;
  CAN_TxMsg.data[2]=0x3F;
  CAN_TxMsg.data[3]=0x41;
  CAN_TxMsg.data[4]=0x00;
  CAN_TxMsg.data[5]=0x00;
  CAN_TxMsg.data[6]=0x00;
  CAN_TxMsg.data[7]=0xD0;
  CAN.send(&CAN_TxMsg);
}

void iPodOn(){
  CAN_TxMsg.id=0x328;  
  CAN_TxMsg.data[0]=0x42;
  CAN_TxMsg.data[1]=0x96;
  CAN_TxMsg.data[2]=0x02;
  CAN_TxMsg.data[3]=0x42; // B
  CAN_TxMsg.data[4]=0x6C; // l
  CAN_TxMsg.data[5]=0x75; // u
  CAN_TxMsg.data[6]=0x65; // e
  CAN_TxMsg.data[7]=0x53; // S
  CAN.send(&CAN_TxMsg);
  delay(10);

  CAN_TxMsg.data[0]=0x01;
  CAN_TxMsg.data[3]=0x61; // a
  CAN_TxMsg.data[4]=0x61; // a
  CAN_TxMsg.data[5]=0x62; // b
  CAN_TxMsg.data[6]=0x20; // _
  CAN_TxMsg.data[7]=0x20; // _
  CAN.send(&CAN_TxMsg);
  delay(10);
}

void iPodOff(){
}

void PrintBus(){
  if (CAN_RxMsg.id==0x3C0){
    Serial.print(CAN_RxMsg.id,HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[0],HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[1],HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[2],HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[3],HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[4],HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[5],HEX);
    Serial.print(";");
    Serial.print(CAN_RxMsg.data[6],HEX);
    Serial.print(";");
    Serial.println(CAN_RxMsg.data[7],HEX);
  }
}

Monday, September 24, 2012

Saab I-BUS info...

So it took me a day or so to figure out that the Saab I-Bus talks on a non-standard speed: 47.619Kbps. Most other CANBUS interfaces talk at 50, 100, 125, 250, 500, or 1000Kbps (1MBit). It's easy to change the speed, but there are many factors that affect the speed in the CANBUS chip/module.

Inside the CAN folder, there is a file called CAN.cpp. You need to edit the CAN.cpp file with Notepad or similar.

Scroll to where it says “switch(speed)”, and you should see several chunks of code that reference “case 1” and “case 500” and a few more. Anytime after the “{“ and before the next “}”, between the other cases, add the following code and save the file:

case 47:
mcp2515_write_register(CNF1,0xC7);
mcp2515_write_register(CNF2,0xBE);
mcp2515_write_register(CNF3,0x04);
#if (DEBUGMODE==1)
Serial.println("Speed=47.619Kps");
#endif
break;

You can see how each case designates a case (I called mine 47 because the Saab I-Bus communicates at 47.619kbps), and by default, this software is not setup to allow us to communicate at the same speed as our Saab I-Bus. So all we're doing here is changing the speed parameters and assigning that speed to “47”. You'll notice that in the Arduino code, I called “CAN.begin(47)”. This is where the Arduino tells the Secuduino to talk to the I-Bus at 47.619kbps.

The "CNF1", "CNF2", and "CNF3" values (registers) we set tell the MCP2515 to talk at 47.619Kbps. These values are different for each speed setting. You can look at the datasheet for this chip and try to decipher it all; it's hard for me to grasp most of it :P

The site http://www.kvaser.com/en/support/bit-timing-calculator.html has a calculator to help you choose the proper register values for what speed and clock values you give it.

Probably more information than you needed/wanted to know, but I think it's good to share none the less.

BlueSaab Software

Here's the code that you can copy & paste into Arduino & write to your chip.

// **START COPY HERE**
// ----------------------------------------------
// SECUDUINO
// http://secuduino.blogspot.com/
// By Igor Real
// 16/05/2011
// 
// Saab CDC Changer Emulator
// Seth Evans
// 23 Sep 2012
// ----------------------------------------------

#include <CAN.h>

int cdbutton;

void setup() {
  // set up CAN
  CAN.begin(47);  // Saab I-Bus is 47.619kbps
  Serial.begin(115200); 
  cdbutton = 0;
  CAN_TxMsg.header.rtr=0;  // this value never changes
  CAN_TxMsg.header.length=8;  // this value never changes
}

void loop() {
  CDC();
  delay(50);
  if (CAN.CheckNew()){
    CAN_TxMsg.data[0]++;
    CAN.ReadFromDevice(&CAN_RxMsg);
    /*if (CAN_RxMsg.id==0x3C0){
     Serial.print(CAN_RxMsg.id,HEX);
     Serial.print(";");
     Serial.print(CAN_RxMsg.data[0],HEX);
     Serial.print(";");
     Serial.print(CAN_RxMsg.data[1],HEX);
     Serial.print(";");
     Serial.print(CAN_RxMsg.data[2],HEX);
     Serial.print(";");
     Serial.print(CAN_RxMsg.data[3],HEX);
     Serial.print(";");
     Serial.print(CAN_RxMsg.data[4],HEX);
     Serial.print(";");
     Serial.print(CAN_RxMsg.data[5],HEX);
     Serial.print(";");
     Serial.print(CAN_RxMsg.data[6],HEX);
     Serial.print(";");
     Serial.println(CAN_RxMsg.data[7],HEX);
     }*/

    //if (CAN_RxMsg.id==0x3C0 && CAN_RxMsg.data[0]==0x80 && CAN_RxMsg.data[1]==0x24){
    if (CAN_RxMsg.id==0x3C0 && CAN_RxMsg.data[1]==0x24){
      cdbutton = 1;
      //Serial.print("cdbutton = ");
      //Serial.println(cdbutton);
      Serial.println("CDC");
    }

    //if (CAN_RxMsg.id==0x3C0 && CAN_RxMsg.data[0]==0x80 && CAN_RxMsg.data[1]==0x14){
    else if (CAN_RxMsg.id==0x3C0 && CAN_RxMsg.data[1]==0x14){
      cdbutton = 0;    
      //Serial.print("cdbutton = ");
      //Serial.println(cdbutton);
      Serial.println("Radio");
    }
  }
  if (cdbutton==1){
    ipod();
  }
}

void CDC(){
  CAN_TxMsg.id=0x3C8;     
  CAN_TxMsg.data[0]=0x20;
  CAN_TxMsg.data[1]=0x00;  
  CAN_TxMsg.data[2]=0x17; 
  CAN_TxMsg.data[3]=0x45;
  CAN_TxMsg.data[4]=0x33;
  CAN_TxMsg.data[5]=0x01;
  CAN_TxMsg.data[6]=0x53;
  CAN_TxMsg.data[7]=0xD0;
  CAN.send(&CAN_TxMsg);
}

void ipod(){
  CAN_TxMsg.id=0x328;     
  CAN_TxMsg.data[0]=0x42;
  CAN_TxMsg.data[1]=0x96;  
  CAN_TxMsg.data[2]=0x02; 
  CAN_TxMsg.data[3]=0x42; // B
  CAN_TxMsg.data[4]=0x6C; // l
  CAN_TxMsg.data[5]=0x75; // u
  CAN_TxMsg.data[6]=0x65; // e
  CAN_TxMsg.data[7]=0x53; // S
  CAN.send(&CAN_TxMsg);
  delay(9);

  CAN_TxMsg.id=0x328;     
  CAN_TxMsg.data[0]=0x01;
  CAN_TxMsg.data[1]=0x96;  
  CAN_TxMsg.data[2]=0x02; 
  CAN_TxMsg.data[3]=0x61; // a
  CAN_TxMsg.data[4]=0x61; // a
  CAN_TxMsg.data[5]=0x62; // b
  CAN_TxMsg.data[6]=0x20; // _
  CAN_TxMsg.data[7]=0x20; // _
  CAN.send(&CAN_TxMsg);
  //delay(10);
  /*
  CAN_TxMsg.id=0x328;     
   CAN_TxMsg.data[0]=0x00;
   CAN_TxMsg.data[1]=0x96;  
   CAN_TxMsg.data[2]=0x02; 
   CAN_TxMsg.data[3]=0x20; // _
   CAN_TxMsg.data[4]=0x00;
   CAN_TxMsg.data[5]=0x00;
   CAN_TxMsg.data[6]=0x00;
   CAN_TxMsg.data[7]=0x00;
   CAN.send(&CAN_TxMsg);
   //delay(10);
   */
}
// **STOP COPY HERE**

Diagrams & Schematics


Here's an overall idea of what we're going to build.


















Here's the CD Changer connector that we're hacking into:













Here's how we're going to connect the Ground Loop Isolator to connect our unbalanced audio out (from the bluetooth module) to the vehicle (balanced audio).






Friday, September 21, 2012

A new upgrade to an old friend

I'll be posting information on how to add an auxiliary input to your 1998 – 2002 Saab 9-3 and 2003 9-3 Convertible, 1994 – 1998 Saab 900 (?) (and similar vehicles), by using the CD Changer (CDC) lines in the trunk. These lines need to be turned on by the car. The car turns these lines on when a special code is sent along the I-Bus. This isn't terribly hard, and not even terribly expensive.

I got most of my information from the following site:
http://pikkupossu.1g.fi/tomi/projects/i-bus/i-bus.html#530

And the few pieces of hardware I'm using:
SecuDuino CANBUS adapter (~$50)

RBBB Kit ($17)(any Arduino board will work)

FTDI cable ($15)(like this one, but there are several available)

Bluetooth stereo audio receiver ($25)

Stereo ground loop isolator ($40)

Other than that, it's just a matter of connecting everything and programming the Arduino. Once that's done, you just power everything up, connect your device to the bluetooth device, and play your music!