Monday, September 22, 2014

Bluetooth schematics and pcb

As requested, here are the schematics and pcb layout I have so far.

Eagle Schematic
Eagle PCB

This version works, BUT still has issues that I've talked about in the previous posts.



Saturday, May 17, 2014

Working bluetooth prototype!

Here's my working prototype bluetooth module. I'm using the BC05B bluetooth module, which works great.

It still has a lot of bugs, and I may have to redesign/add some things.

PROS:

  1. steering wheel controls for next track & beginning of song/previous track!
  2. 1 device for bluetooth audio/control; NO WIRES!
  3. sound quality is very good (IMHO)
  4. module connects to the last-known-connected device automatically, if it's within range (i.e., if my iPhone 5 was the last device that the module was connected to before I left the car, when I return to the car, it automatically connects when I'm back within range)
  5. when switching to aux mode, the currently connected device starts playing automatically
  6. when switching to radio mode, the currently playing, connected device stops playing (hits the pause button for you so you don't "lose your place" in the track)


CONS:
  1. The volume turns on at medium; i'm trying to get the device to turn the volume up full at power on but no luck yet
  2. still not 100% sure on the pairing sequence for to this module:

  • pairing a new-to-the-module device
  • pairing a new-to-the-module device when the module is currently connected to a different device
  • connecting a known device to the module
  • connecting a known device to the module when the module is currently connected to a diff device

No artist/track info available yet (that i'm aware of) for displaying on the SID; will look into this after I get all the other bugs worked out.

The bottom of my pcb is my attempt to get the volume to turn up at power on, but it's not working, I think because of software...could be the 4066 chip I piggy-backed on top of the other :)

Right now, I have the SRC button on the steering wheel set to put the module into discoverable mode (this only works if you're in Aux/CD changer mode, so you can still use SRC to change between FM1, FM2, and AM1). This has worked pretty good so far between switching connected devices; i.e., when my wife wants to play her music through the car, I hit the SRC button while playing my music, it disconnects from my phone, stops the music [the head unit kicks out of aux mode, back to the radio], the module goes into discoverable mode, she connects to it, and I hit CD twice to get back to aux mode, and it automatically starts playing the last thing she was listening to.

enough talk, pictures!



Sunday, March 2, 2014

How to build your own 3.5mm version of BlueSaab

I get a lot of emails from people on how to build one of these things so here are directions. This involves soldering through-hole components and uploading code to the microprocessor with Arduino.

Buy the parts here (~$40) or anywhere else you find similar stuff:
http://www.mouser.com/ProjectManager/ProjectDetail.aspx?AccessID=89c9334eae

If something is out of stock, you'll either have to wait for more to be ordered or delete it from your cart and buy a suitable substitute. i.e., sometimes the atmega328p is out of stock so I order the atmega328.

If you don't want to buy the blank atmega328 chip, you can pay extra for a 328 with a bootloader preinstalled ($5):
or

Buy one of these ($5):

Buy a CD changer connector ($7/ea, minimum 2, plus S/H: ~$20):

Order the PCB (minimum quantity 3, for $30 shipped):
It takes about 3 weeks for them to get fabbed and shipped.

You'll also need/want 4 screws to hold the PCB to the enclosure; I use 4, 5/16" panhead screws.
I also secure the CD changer connector to the PCB with 1/2" long 4/40 panhead screws and 4/40 locknuts.
  1. flash the bootloader if necessary (I'm not going to cover how to do that here; google it)
  2. Assemble the PCB
  3. Wire up the 3.5mm jack to the PCB with ~5" of wire for each pole
  4. Cut a hole in the enclosure for the CD changer connector and a 1/4" hole for the panel-mount 3.5mm jack.
  5. upload the code with an FTDI adapter/cable
  6. plug it into the cd changer harness (using the adapter cable, if necessary for 9-5 people)
  7. route the 3.5mm cable from the module to the front of the car under the carpet/door sills
You'll probably have to contact me regarding flashing the code...just FYI ;)

To get to the aux in on the head unit, hit the CD button twice. If you have a tape player and cd player (9-5 people), hit CD 3 times. You can also hit SRC button on the steering wheel to cycle.

With the minimum quantity orders for some of these items, it's probably a good idea to get together with another Saab buddy and just build 2 or 3 of them.

Newest version of code

// ----------------------------------------------
// SECUDUINO
// http://secuduino.blogspot.com/
// By Igor Real
// 16/05/2011
//
// Saab CDC Changer Emulator
// http://BlueSaab.blogspot.com/
// By Seth Evans
// 29 July 2013
// 19 Feb 2014 - added name, sid text, date to output
// ----------------------------------------------

#include <CAN.h>

int cdbutton = 0;
int toggleshuffle = 1;
int mute = 0;
int CDCcmd[] = {
  0xE0,0x00,0x3F,0x31,0xFF,0xFF,0xFF,0xD0};
// first byte needs to be 32 otherwise 2004 9-5 will throw airbag light; it was 0x62
int ninefivecmd[] = {
  0x32,0x00,0x00,0x16,0x01,0x02,0x00,0x00};
int beep[] = {
  0x80,0x04,0x00,0x00,0x00,0x00,0x00,0x00};
int playipod[] = {
  0xFF,0x55,0x04,0x02,0x00,0x00,0x01,0xF9};
int playpauseipod[] = {
  0xFF,0x55,0x03,0x02,0x00,0x01,0xFA};
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
  Serial.println("9-5 Test Code 2");
  Serial.println("Seth Evans - 'Aux In'");
  Serial.println("19 Feb 2014");
  delay(2000);
}

void loop() {
  //cdbutton = 1;
  //PrintBus();
  // 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 <= 860; i++) {
    if (CAN.CheckNew()) {
      CAN_TxMsg.data[0]++;
      CAN.ReadFromDevice(&CAN_RxMsg);
      //PrintBus();
      if (CAN_RxMsg.id==0x6A1) {
        CAN_TxMsg.id=0x6A2;     // CD Changer
        for (int c = 0; c < 8; c++) {
          CAN_TxMsg.data[c]=ninefivecmd[c];
        }
        CAN.send(&CAN_TxMsg);
      }

      if (CAN_RxMsg.id==0x3C0) {
        if (CAN_RxMsg.data[0]==0x80) {
          switch (CAN_RxMsg.data[1]) {
          case 0x24:
            cdbutton = 1;
            for (int j = 0; j < 8; j++) {
              CAN_TxMsg.id=0x430;
              CAN_TxMsg.data[j]=beep[j];
            }
            CAN.send(&CAN_TxMsg);
            for (int j = 0; j < 8; j++) {
              Serial.write(byte(playipod[j]));
            }
            delay(3);
            //Serial.println("Release");
            for (int i = 0; i < 7; i++) {
              Serial.write(byte(buttonRelease[i]));
            }
            break;
          case 0x14:
            cdbutton = 0;
            for (int a = 0; a <=2; a++) {
              for (int j = 0; j < 8; j++) {
                CAN_TxMsg.id=0x430;
                CAN_TxMsg.data[j]=beep[j];
              }
              CAN.send(&CAN_TxMsg);
            }
            for (int j = 0; j < 8; j++) {
              Serial.write(byte(stopipod[j]));
            }
            delay(3);
            //Serial.println("Release");
            for (int i = 0; i < 7; i++) {
              Serial.write(byte(buttonRelease[i]));
            }
            //Serial.println("Radio");
            break;
          }
          if (cdbutton == 1) {
            switch (CAN_RxMsg.data[1]) {
            case 0x59: // NXT button signal
              for (int j = 0; j < 7; j++) {
                Serial.write(byte(playpauseipod[j]));
              }
              break;
            case 0x76: // Long press of CD/RDM button
              if (toggleshuffle > 3) {
                toggleshuffle = 1;
              }
              switch (toggleshuffle) {
              case 1:
                for (int j = 0; j < 9; j++) {
                  Serial.write(byte(repeat[j]));
                }
                break;
              case 2:
                for (int j = 0; j < 9; j++) {
                  Serial.write(byte(repeat[j]));
                }
                break;
              case 3:
                for (int j = 0; j < 9; j++) {
                  Serial.write(byte(repeat[j]));
                }
                for (int j = 0; j < 8; j++) {
                  Serial.write(byte(shuffle[j]));
                }
                break;
              }
              toggleshuffle++;
              //break;
            case 0xB1: // Audio mute on?
              for (int j = 0; j < 8; j++) {
                Serial.write(byte(stopipod[j]));
              }
              break;
            case 0xB0: // Audio mute off?
              for (int j = 0; j < 8; j++) {
                Serial.write(byte(playipod[j]));
              }
              break;
            case 0x35: // Seek next (Seek+)?
              for (int j = 0; j < 7; j++) {
                Serial.write(byte(next[j]));
              }
              break;
            case 0x36: // Seek previous (Seek-)?
              for (int j = 0; j < 7; j++) {
                Serial.write(byte(prev[j]));
              }
              break;
            }
            delay(3);
            //Serial.println("Release");
            for (int i = 0; i < 7; 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 0x04: // NXT button on wheel
              //for (int j = 0; j < 9; j++) {
              //Serial.write(byte(repeat[j]));
              //}
              //break;
            case 0x10: // Seek+ button on wheel
              //Serial.println("Next");
              for (int j = 0; j < 7; j++) {
                Serial.write(byte(next[j]));
              }
              break;
            case 0x08: // Seek- button on wheel
              //Serial.println("Prev");
              for (int k = 0; k < 7; k++) {
                Serial.write(byte(prev[k]));
              }
              break;
            }
            delay(3);
            //Serial.println("Release");
            for (int i = 0; i < 7; 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);
}

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]=0x20; // _
  CAN_TxMsg.data[5]=0x20; // _
  CAN_TxMsg.data[6]=0x41; // A
  CAN_TxMsg.data[7]=0x75; // u
  CAN.send(&CAN_TxMsg);
  delay(10);

  CAN_TxMsg.data[0]=0x01; // message 1
  CAN_TxMsg.data[3]=0x78; // x
  CAN_TxMsg.data[4]=0x20; // _
  CAN_TxMsg.data[5]=0x49; // I
  CAN_TxMsg.data[6]=0x6E; // n
  CAN_TxMsg.data[7]=0x20; // _
  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; // 11
  CAN_TxMsg.data[1]=0x02; // Row 2?
  CAN_TxMsg.data[2]=0x05; // 05
  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 PrintBus() {
  if (CAN_RxMsg.id==0x6A2) {
  //if (CAN_RxMsg.data[0]==0x80) {
    Serial.print(CAN_RxMsg.id,HEX);
    Serial.print(";");
    for (int i = 0; i < 8; i++) {
      Serial.print(CAN_RxMsg.data[i],HEX);
      Serial.print(";");
    }
    Serial.println("");
  }
}

Sunday, January 12, 2014

CD Changer Connectors

I recently found out that "The Connector People" sells these connectors online via their website for under $7! I think they have a minimum order of 2...



http://connectorpeople.com/Connector/TYCO-AMP-TE_CONNECTIVITY/8/827229-1

Awesome!

Thursday, January 9, 2014

9-5 adapter harness

I've been told by a few people who have 9-5's, that certain years (not sure which) didn't come with the "regular" cd changer plug; Saab made an adapter though, that converts those cars from a 2-plug harness into the single plug connector.

eEuroParts sells them for less than $12.

I wish I could tell you which years need this cable, but it shouldn't be too hard to figure out if your 9-5 has 2 connectors or 1 under the rear carpet.

Wednesday, January 8, 2014

ATMega328 with bootloader

A reader, Jeff, made a good find that I didn't even think about; Mouser sells ATMega328's with the Arduino bootloader preinstalled! I'm not for sure which bootloader is installed, but it should work.

http://www.mouser.com/ProductDetail/Arduino/A000048/?qs=sGAEpiMZZMs0PWRNvpRp0N8bFGqoofv%2f

Sparkfun sells one with the Optiboot bootloader

https://www.sparkfun.com/products/10524

Thanks for the good find, man! :)

Bluetooth parts list

It's been requested I post the parts list for the bluetooth board, so here it is, minus the CD changer connector of course.

http://www.mouser.com/ProjectManager/ProjectDetail.aspx?AccessID=9cf0c26186

And the bluetooth module is still needed as well.

The code is still in work; I should have my bluetooth modules in a week or so; the first shipment I ordered (50 days ago!) got lost, so I had to ask for a replacement.