Saturday, July 20, 2013
Populated board
Thought I would show what a fully populated board looks like when it's ready to be put into the box (minus wires).
Update to new code
I found out the other day that the newest version of code prevents Night Panel from working, or allowing you to use OpenSID.
I think the only difference is the 348 message, bit 2, being a 1 instead of a 5. I think when it's a 1, that is the highest priority, as if Night Panel were on (no gauges lit) & your coolant temp got too hot, your SID would light up & tell you. So I think it's probably better to leave this message bit higher & just deal with flashing SID txt for a few seconds.
Monday, July 15, 2013
Fabbed circuit boards!
Got my first run of fabbed circuit boards today; they look sick! I'll def be ordering more boards through OSHPark.com :)
Friday, July 12, 2013
updated code
Here's some updated code. This should fix the flickering of the SID text when switching to Aux mode. Also removed the 0x368 messages - Henrik informed me that they weren't needed. Not much else changed I don't think. This still includes the main CDC code as well as the extra code for 9-5's.
// ----------------------------------------------
// SECUDUINO
// http://secuduino.blogspot.com/
// By Igor Real
// 16/05/2011
//
// Saab CDC Changer Emulator
// http://BlueSaab.blogspot.com/
// By Seth Evans
// 12 July 2013
// ----------------------------------------------
#include <CAN.h>
int cdbutton = 0;
int toggleshuffle = 1;
int mute = 0;
int CDCcmd[] = {
0xE0,0x00,0x3F,0x31,0xFF,0xFF,0xFF,0xD0};
int ninefivecmd[] = {
0x62,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");
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++) {
Serial.write(byte(beep[j]));
}
for (int j = 0; j < 9; j++) {
Serial.write(byte(playipod[j]));
}
delay(3);
//Serial.println("Release");
for (int i = 0; i < 8; i++) {
Serial.write(byte(buttonRelease[i]));
}
break;
case 0x14:
cdbutton = 0;
for (int a = 1; a <=2; a++) {
for (int j = 0; j < 8; j++) {
Serial.write(byte(beep[j]));
}
}
for (int j = 0; j < 9; j++) {
Serial.write(byte(stopipod[j]));
}
delay(3);
//Serial.println("Release");
for (int i = 0; i < 8; 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 < 10; 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 < 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;
case 0xB1: // Audio mute on?
for (int j = 0; j < 9; j++) {
Serial.write(byte(stopipod[j]));
}
break;
case 0xB0: // Audio mute off?
for (int j = 0; j < 9; j++) {
Serial.write(byte(playipod[j]));
}
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 0x04: // NXT button on wheel
//for (int j = 0; j < 10; j++) {
//Serial.write(byte(repeat[j]));
//}
//break;
case 0x10: // Seek+ button on wheel
//Serial.println("Next");
for (int j = 0; j < 8; j++) {
Serial.write(byte(next[j]));
}
break;
case 0x08: // Seek- button on wheel
//Serial.println("Prev");
for (int k = 0; k < 8; k++) {
Serial.write(byte(prev[k]));
}
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);
}
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]=0x3C; // <
CAN_TxMsg.data[5]=0x42; // B
CAN_TxMsg.data[6]=0x6C; // l
CAN_TxMsg.data[7]=0x75; // u
CAN.send(&CAN_TxMsg);
delay(10);
CAN_TxMsg.data[0]=0x01; // message 1
CAN_TxMsg.data[3]=0x65; // e
CAN_TxMsg.data[4]=0x74; // t
CAN_TxMsg.data[5]=0x6F; // o
CAN_TxMsg.data[6]=0x6F; // o
CAN_TxMsg.data[7]=0x74; // t
CAN.send(&CAN_TxMsg);
delay(10);
CAN_TxMsg.data[0]=0x00; // message 0
CAN_TxMsg.data[3]=0x68; // h
CAN_TxMsg.data[4]=0x3E; // >
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]=0x01; // 05 01 seems to work perfect
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==0x3b0) {
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("");
//}
}
// ----------------------------------------------
// SECUDUINO
// http://secuduino.blogspot.com/
// By Igor Real
// 16/05/2011
//
// Saab CDC Changer Emulator
// http://BlueSaab.blogspot.com/
// By Seth Evans
// 12 July 2013
// ----------------------------------------------
#include <CAN.h>
int cdbutton = 0;
int toggleshuffle = 1;
int mute = 0;
int CDCcmd[] = {
0xE0,0x00,0x3F,0x31,0xFF,0xFF,0xFF,0xD0};
int ninefivecmd[] = {
0x62,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");
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++) {
Serial.write(byte(beep[j]));
}
for (int j = 0; j < 9; j++) {
Serial.write(byte(playipod[j]));
}
delay(3);
//Serial.println("Release");
for (int i = 0; i < 8; i++) {
Serial.write(byte(buttonRelease[i]));
}
break;
case 0x14:
cdbutton = 0;
for (int a = 1; a <=2; a++) {
for (int j = 0; j < 8; j++) {
Serial.write(byte(beep[j]));
}
}
for (int j = 0; j < 9; j++) {
Serial.write(byte(stopipod[j]));
}
delay(3);
//Serial.println("Release");
for (int i = 0; i < 8; 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 < 10; 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 < 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;
case 0xB1: // Audio mute on?
for (int j = 0; j < 9; j++) {
Serial.write(byte(stopipod[j]));
}
break;
case 0xB0: // Audio mute off?
for (int j = 0; j < 9; j++) {
Serial.write(byte(playipod[j]));
}
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 0x04: // NXT button on wheel
//for (int j = 0; j < 10; j++) {
//Serial.write(byte(repeat[j]));
//}
//break;
case 0x10: // Seek+ button on wheel
//Serial.println("Next");
for (int j = 0; j < 8; j++) {
Serial.write(byte(next[j]));
}
break;
case 0x08: // Seek- button on wheel
//Serial.println("Prev");
for (int k = 0; k < 8; k++) {
Serial.write(byte(prev[k]));
}
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);
}
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]=0x3C; // <
CAN_TxMsg.data[5]=0x42; // B
CAN_TxMsg.data[6]=0x6C; // l
CAN_TxMsg.data[7]=0x75; // u
CAN.send(&CAN_TxMsg);
delay(10);
CAN_TxMsg.data[0]=0x01; // message 1
CAN_TxMsg.data[3]=0x65; // e
CAN_TxMsg.data[4]=0x74; // t
CAN_TxMsg.data[5]=0x6F; // o
CAN_TxMsg.data[6]=0x6F; // o
CAN_TxMsg.data[7]=0x74; // t
CAN.send(&CAN_TxMsg);
delay(10);
CAN_TxMsg.data[0]=0x00; // message 0
CAN_TxMsg.data[3]=0x68; // h
CAN_TxMsg.data[4]=0x3E; // >
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]=0x01; // 05 01 seems to work perfect
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==0x3b0) {
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("");
//}
}
Wednesday, June 5, 2013
New board design
I decided to fix up the board design. I added a jumper underneath the 328P socket, but I added all the holes for the unused pins on all the DIP sockets, so no more cutting off pins! :)
REMEMBER : This view of the board is the BOTTOM, so all the components go on the other side, and that means you have to watch out for pin 1 on all the ICs! It's a little confusing, even for me. I just hold the board like it looks below and hold the IC in the air, as it were going to match the image, and then flip the two over, and it should have the proper orientation. It also helps a lot to watch for the pin 1 indicator; the indentation in the sockets.
The reason I designed the board like this is because it makes it easier for me to manufacture the boards. I mill them out, and of course the side you mill has to be "up", so this way I don't have to reverse the file when I export it out to the routing program.
Schematic file
Board file
REMEMBER : This view of the board is the BOTTOM, so all the components go on the other side, and that means you have to watch out for pin 1 on all the ICs! It's a little confusing, even for me. I just hold the board like it looks below and hold the IC in the air, as it were going to match the image, and then flip the two over, and it should have the proper orientation. It also helps a lot to watch for the pin 1 indicator; the indentation in the sockets.
The reason I designed the board like this is because it makes it easier for me to manufacture the boards. I mill them out, and of course the side you mill has to be "up", so this way I don't have to reverse the file when I export it out to the routing program.
Schematic file
Board file
Sunday, June 2, 2013
Compatibility
I think this module is compatible with the following series:
1998 - 2002 9-3
1998 - 2003 9-3 convertible
1998 - 2005 9-5
Don't hold me to those for sure, especially the 9-5 series; I'm a 9-3 guy :)
For sale?
I'm curious to see how many people would be interested in buying one of these units for $100, shipping included, and you would get to customize your SID text (12 characters max)!
I would include the Radio Shack connector, CU3242MB case, and it would pretty much be ready to go, just need installed. It's not exactly plug-n-play though; obviously you would need to cut off your CD Changer connector & replace it with the radio shack connector. Also, the way I've been doing these is putting a RJ45 female on the audio in (from ipod/mp3/whatever) and ipod control lines (5 total wires), and then making another female RJ45 to 3.5mm (or an ipod breakout connector if you want ipod control). I would leave that end up to you. This would allow you to get any old ethernet/CAT5 cable laying around and route it under your trim panels from the trunk to the front of the car. Then you can decide how/where you want your input to be at.
A LOT of this "kit" would be custom, BY YOU. I would only supply the Aux-In unit.
What do you think??
I would include the Radio Shack connector, CU3242MB case, and it would pretty much be ready to go, just need installed. It's not exactly plug-n-play though; obviously you would need to cut off your CD Changer connector & replace it with the radio shack connector. Also, the way I've been doing these is putting a RJ45 female on the audio in (from ipod/mp3/whatever) and ipod control lines (5 total wires), and then making another female RJ45 to 3.5mm (or an ipod breakout connector if you want ipod control). I would leave that end up to you. This would allow you to get any old ethernet/CAT5 cable laying around and route it under your trim panels from the trunk to the front of the car. Then you can decide how/where you want your input to be at.
A LOT of this "kit" would be custom, BY YOU. I would only supply the Aux-In unit.
What do you think??
Subscribe to:
Posts (Atom)



