Wednesday, February 13, 2013

Newest update

Ok, a lot's changed since my last update.

I've added some things, deleted some things, but everything is working better than before. The new code below incorporates iPod control via the steering wheel (next track, previous track, random & shuffle toggles), and cleaner code. I had attempted to get text to scroll across the SID, but I guess that was too much information because nothing would display at all. Maybe in a future update. I have the working code to make the SID scroll text on a work bench, but it doesn't operate the same way in the car; probably too many CAN messages or something.


// ----------------------------------------------
// SECUDUINO
// http://secuduino.blogspot.com/
// By Igor Real
// 16/05/2011
//
// Saab CDC Changer Emulator
// http://BlueSaab.blogspot.com/
// By Seth Evans
// 23 Jan 2013
// ----------------------------------------------

#include <CAN.h>

int cdbutton = 0;
int toggleshuffle = 1;
int CDCcmd[] = {
  0xE0,0x00,0x3F,0x31,0xFF,0xFF,0xFF,0xD0};
int playipod[] = {
  0xFF,0x55,0x04,0x02,0x00,0x00,0x01,0xF9};
int stopipod[] = {
  0xFF,0x55,0x04,0x02,0x00,0x00,0x02,0xF8};
int next[] = {
  0xFF,0x55,0x03,0x02,0x00,0x08,0xF3};
int prev[] = {
  0xFF,0x55,0x03,0x02,0x00,0x10,0xEB};
int shuffle[] = {
  0xFF,0x55,0x04,0x02,0x00,0x00,0x80,0x7A};
int repeat[] = {
  0xFF,0x55,0x05,0x02,0x00,0x00,0x00,0x01,0xF8};
int buttonRelease[] = {
  0xFF,0x55,0x03,0x02,0x00,0x00,0xFB};

void setup() {
  // set up CAN
  CAN.begin(47);  // Saab I-Bus is 47.619kbps
  Serial.begin(9600);
  //cdbutton = 0;
  //toggleshuffle = 1;
  CAN_TxMsg.header.rtr=0;     // this value never changes
  CAN_TxMsg.header.length=8;  // this value never changes
  // not sure if this is needed; pauses program before it loops
  delay(2000);
}

void loop() {
  // CDC code needs sent every second or less so all loops
  // running added together need to take less than 1000ms
  // but no more or the car won't "see" the CDC
  CDC();
  for (int i = 0; i <= 800; i++) {
    if (CAN.CheckNew()) {
      CAN_TxMsg.data[0]++;
      CAN.ReadFromDevice(&CAN_RxMsg);
      //PrintBus();
      if (CAN_RxMsg.id==0x3C0) {
        if (CAN_RxMsg.data[0]==0x80) {
          switch (CAN_RxMsg.data[1]) {
          case 0x24:
            cdbutton = 1;
            //Serial.println("CDC");
            for (int j = 0; j < 9; j++) {
              Serial.write(byte(playipod[j]));
            }
            break;
          case 0x14:
            cdbutton = 0;  
            for (int j = 0; j < 9; j++) {
              Serial.write(byte(stopipod[j]));
            }
            //Serial.println("Radio");
            break;
          }
          delay(3);
          //Serial.println("Release");
          for (int i = 0; i < 8; i++) {
            Serial.write(byte(buttonRelease[i]));
          }
        }
      }
      if (CAN_RxMsg.id==0x290) {
        if (CAN_RxMsg.data[0]==0x80) {
          if (cdbutton == 1) {
            switch (CAN_RxMsg.data[2]) {
            case 0x10:
              //Serial.println("Next");
              for (int j = 0; j < 8; j++) {
                Serial.write(byte(next[j]));
              }
              break;
            case 0x08:
              //Serial.println("Prev");
              for (int k = 0; k < 8; k++) {
                Serial.write(byte(prev[k]));
              }
              break;
            case 0x04:
              if (toggleshuffle > 3) {
                toggleshuffle = 1;
              }
              switch (toggleshuffle) {
              case 1:
                for (int j = 0; j < 10; j++) {
                  Serial.write(byte(repeat[j]));
                }
                break;
              case 2:
                for (int j = 0; j < 10; j++) {
                  Serial.write(byte(repeat[j]));
                }
                break;
              case 3:
                for (int j = 0; j < 10; j++) {
                  Serial.write(byte(repeat[j]));
                }
                for (int j = 0; j < 9; j++) {
                  Serial.write(byte(shuffle[j]));
                }
                break;
              }
              toggleshuffle++;
              break;
            }
            delay(3);
            //Serial.println("Release");
            for (int i = 0; i < 8; i++) {
              Serial.write(byte(buttonRelease[i]));
            }
          }
        }
      }
    }
    delay(1);
  }
  if (cdbutton==1) {
    //Serial.println("iPod ON");
    iPodOn();
  }
  else {
    Serial.println("iPod OFF");
    //iPodOff();
    //delay(400);
  }
}

void CDC() {
  CAN_TxMsg.id=0x3C8;     // CD Changer
  for (int c = 0; c < 8; c++) {
    CAN_TxMsg.data[c]=CDCcmd[c];
  }
  CAN.send(&CAN_TxMsg);
  /*CAN_TxMsg.id=0x3C8;     // CD Changer
   CAN_TxMsg.data[0]=0xE0;
   CAN_TxMsg.data[1]=0x00;
   CAN_TxMsg.data[2]=0x3F; // all 6 discs inserted
   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() {
  // This loop takes 50ms
  CAN_TxMsg.id=0x328;     // SID audio text
  CAN_TxMsg.data[0]=0x42; // message 2
  CAN_TxMsg.data[1]=0x96;
  CAN_TxMsg.data[2]=0x02; // Row 2
  CAN_TxMsg.data[3]=0x20; // _
  CAN_TxMsg.data[4]=0x69; // i
  CAN_TxMsg.data[5]=0x53; // S
  CAN_TxMsg.data[6]=0x61; // a
  CAN_TxMsg.data[7]=0x61; // a
  CAN.send(&CAN_TxMsg);
  delay(10);

  CAN_TxMsg.data[0]=0x01; // message 1
  CAN_TxMsg.data[3]=0x62; // b
  CAN_TxMsg.data[4]=0x20; // _
  CAN_TxMsg.data[5]=0x41; // A
  CAN_TxMsg.data[6]=0x75; // u
  CAN_TxMsg.data[7]=0x78; // x
  CAN.send(&CAN_TxMsg);
  delay(10);

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

  CAN_TxMsg.id=0x348;     // audio text control
  CAN_TxMsg.data[0]=0x11;
  CAN_TxMsg.data[1]=0x02; // Row 2
  CAN_TxMsg.data[2]=0x05;
  CAN_TxMsg.data[3]=0x18; // priority 18?
  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=0x368;     // SID text priority
  CAN_TxMsg.data[0]=0x02; // Row 2
  CAN_TxMsg.data[1]=0x18; // priority 18?
  CAN_TxMsg.data[2]=0x00;
  CAN_TxMsg.data[3]=0x00;
  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 iPodOff() {
  // I don't think this code is needed at all...
  CAN_TxMsg.id=0x368;     // turn off the SID (SPA) text
  CAN_TxMsg.data[0]=0x02;
  CAN_TxMsg.data[1]=0x19;
  CAN_TxMsg.data[2]=0x00;
  CAN_TxMsg.data[3]=0x00;
  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==0x290) {
    Serial.print(CAN_RxMsg.id,HEX);
    Serial.print(";");
    for (int i = 0; i < 8; i++) {
      Serial.print(CAN_RxMsg.data[i],HEX);
      Serial.print(";");
    }
  }
}

1 comment:

  1. Hi, nice project! I stumbled upon this blog a few months back, while working on something quite similar. Having read your previous posts and you code, maybe I can add a few suggestions:
    1) Use CAN filters. Since you're only ever interested in receiving messages with id 0x290 and 0x3c8, set up receive filters at the beginning and don't waste time reading in all the non-relevant traffic.
    2) You're right in that you don't need the iPodOff code at all. As it is, the iPodOn code is sort of a diversion hack into Radio/SID communication, so as soon as you stop sending that, it will revert back to normal by itself.
    3) It would be better to use timer interrupts for the CDC message and not worry about the loop timing in other parts of the code. Right now the timing depends heavily on the can bus and serial traffic. Interrupts need special considerations though, but maybe something to consider for the future.
    Hope this helps in some way or another :)

    ReplyDelete