Dodge/Chrysler Radio Emulator Connect to Interior CAN bus

Dependencies:   mbed SDFileSystem

Revision:
0:9bc41d70bdd3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/radioEmulator.cpp	Mon Jan 31 05:13:04 2011 +0000
@@ -0,0 +1,816 @@
+#include "mbed.h"
+#include "radioEmulator.h"
+
+DigitalOut led1(LED1);
+DigitalOut led2(LED2);
+DigitalOut led3(LED3);
+DigitalOut led4(LED4);
+
+//LocalFileSystem local("local");
+#include "SDFileSystem.h"
+
+SDFileSystem sd(p5, p6, p7, p8, "sd"); // the pinout on the mbed Cool Components workshop board
+
+char RadioEmulator::unlock[6] = {0x03,0x02,0x00,0x40,0x87,0xa5};
+char RadioEmulator::lock[6] = {0x01, 0x02, 0x00, 0x40, 0x87, 0xa5};
+char RadioEmulator::trunk[6] = {0x05, 0x02, 0x00, 0x40, 0x87, 0xa5};
+
+RadioEmulator::RadioEmulator()
+{
+    usb = new MODSERIAL(USBTX, USBRX, 1024, 128, NULL); // tx, rx
+    usb->baud(115200);
+    
+    can2 = new CAN(p30,p29);
+    can_RS = new DigitalOut(p28);
+    canIRQ = new InterruptIn(p27);
+
+    serialCounter = 0;
+    prevSWC = 0;
+    radioOn = false;
+    for (int i = 0; i < 8; i++)
+    {
+        for (int j = 0; j < 8; j++)
+        {
+            memset(siriusText[i][j], 0, 8);
+        }
+    }
+    
+//    usb->printf("starting\r\n");
+    
+    memset(&status, 0, sizeof(status));    
+    status._radioMode = SAT;
+//    readInitFile();
+    status._volume = 10;
+    status._bass = 15;
+    status._mid = 13;
+    status._treble = 14;
+    status._balance = 10;
+    status._fade = 10;
+
+    PowerUp();
+    
+    ReceivedHostMsg = false;
+    statusTicker.attach(this, &RadioEmulator::SendStatusToHost, 0.1);
+
+    opMode = standalone;
+    HostTimeout.attach(this, &RadioEmulator::CheckHostTimeout, 1);
+    
+    for (int i = 0; i < 6; i++)
+    {
+        buttonClicks[i] = 0;
+        buttonHeld[i] = false;
+    }
+    reportButtonClick = false;
+    buttonClickTimedOut = false;        
+}
+
+void RadioEmulator::readInitFile()
+{
+    FILE *fp = fopen("/sd/stereo.txt", "r");  // Open "out.txt" on the local file system for writing
+    char temp[100];
+        
+    while ( fscanf(fp, "%s", temp) > 0)
+    {
+        if (strcmp(temp, "volume") == 0)
+        {
+            fscanf(fp, "%d", &status._volume);
+        }
+        if (strcmp(temp, "bass") == 0)
+        {
+            fscanf(fp, "%d", &status._bass);
+        }
+        if (strcmp(temp, "mid") == 0)
+        {
+            fscanf(fp, "%d", &status._mid);
+        }
+        if (strcmp(temp, "treble") == 0)
+        {
+            fscanf(fp, "%d", &status._treble);
+        }
+        if (strcmp(temp, "balance") == 0)
+        {
+            fscanf(fp, "%d", &status._balance);
+        }
+        if (strcmp(temp, "fade") == 0)
+        {
+            fscanf(fp, "%d", &status._fade);
+        }
+    }
+    
+    fclose(fp);
+}
+
+void RadioEmulator::writeInitFile()
+{
+    FILE *fp = fopen("/sd/stereo.txt", "w");  // Open "out.txt" on the local file system for writing
+
+    fprintf(fp,"volume %d\r\n", status._volume);
+    fprintf(fp,"bass %d\r\n", status._bass);
+    fprintf(fp,"mid %d\r\n", status._mid);
+    fprintf(fp,"treble %d\r\n", status._treble);
+    fprintf(fp,"balance %d\r\n", status._balance);
+    fprintf(fp,"fade %d\r\n", status._fade);
+    fclose(fp);
+}
+
+void RadioEmulator::SendOnMsg()
+{
+    CANMessage msg;
+    msg.id = 0x416;
+    msg.len = 8;
+    char temp[8] = {0xfe, 0x1b, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff};
+    memcpy(msg.data, temp, 8);
+    can2->write(msg);
+}
+
+void RadioEmulator::SendRadioModeMsg()
+{
+    CANMessage msg;
+    msg.id = 0x09F;
+    msg.len = 8;
+
+    msg.data[7] = 0x11;
+    msg.data[6] = 0xff;
+    msg.data[5] = 0xff;
+    msg.data[4] = 0xff;
+    msg.data[3] = 0x07;
+    msg.data[2] = 0x00;
+    msg.data[1] = 0x00;
+    msg.data[0] = 0x00;
+
+    if (status._radioMode == AM)
+    {
+        if (status._amPreset != 0)
+        {
+            msg.data[0] = (status._amPreset + 1) << 4;
+        }
+        msg.data[1] = (status._amFreq & 0xFF00) >> 8;
+        msg.data[2] = (status._amFreq & 0x00FF);
+    }
+    else if (status._radioMode == FM)
+    {
+        if (status._fmPreset != 0)
+        {
+            msg.data[0] = (status._fmPreset + 1) << 4;
+        }
+        msg.data[0] = 0x01;
+        msg.data[1] = (status._fmFreq & 0xFF00) >> 8;
+        msg.data[2] = (status._fmFreq & 0x00FF);
+    }
+    else if (status._radioMode == CD)
+    {
+        msg.data[0] = ((status._cdNum + 1) << 4) + 0x3;
+        msg.data[2] = status._cdTrackNum;
+        msg.data[5] = status._cdHours;
+        msg.data[6] = status._cdMinutes;
+        msg.data[7] = status._cdSeconds;
+    }
+    else if (status._radioMode == SAT)
+    {
+        if (status._siriusPreset != 0)
+        {
+            msg.data[0] = (status._siriusPreset + 1) << 4;
+        }
+        msg.data[0] |= 0x04;
+        msg.data[1] = (status._fmFreq & 0xFF00) >> 8;
+        msg.data[2] = status._siriusChan;
+    }
+    else if (status._radioMode == VES)
+    {
+        msg.data[0] = 0x16;
+        msg.data[1] = 0x10;
+        msg.data[2] = 0x01;
+    }
+
+    msg.data[1] |= 0x10;
+
+    can2->write(msg);
+}
+
+void RadioEmulator::SendEVICMsg()
+{
+    CANMessage msg;
+    msg.id = 0x394;
+    msg.len = 6;
+    
+    memset(msg.data, 0, 8);
+
+    if (status._radioMode == AM)
+    {
+        if (status._amPreset != 0)
+        {
+            msg.data[0] = (status._amPreset + 1) << 4;
+        }
+        msg.data[1] = (status._amFreq & 0xFF00) >> 8;
+        msg.data[2] = (status._amFreq & 0x00FF);
+    }
+    else
+    {
+        if (status._fmPreset != 0)
+        {
+            msg.data[0] = (status._fmPreset + 1) << 4;
+        }
+        msg.data[0] = 0x01;
+        msg.data[1] = (status._fmFreq & 0xFF00) >> 8;
+        msg.data[2] = (status._fmFreq & 0x00FF);
+    }
+
+    can2->write(msg);
+}
+
+void RadioEmulator::SendStereoSettingsMsg()
+{
+    CANMessage msg;
+    msg.id = 0x3D0;
+    msg.len = 7;
+
+    msg.data[0] = status._volume;
+    msg.data[1] = status._balance;
+    msg.data[2] = status._fade;
+    msg.data[3] = status._bass;
+    msg.data[4] = status._mid;
+    msg.data[5] = status._treble;
+
+    can2->write(msg);
+}
+
+void RadioEmulator::ChangeSiriusStation(int station, bool turn_on)
+{
+    CANMessage msg;
+    msg.id = 0x3B0;
+    msg.len = 6;
+
+    if (turn_on)
+    {
+        msg.data[0] = 21;
+    }
+    else
+    {
+        msg.data[0] = 23;
+    }
+    msg.data[1] = station;
+
+    can2->write(msg);
+
+    memset(msg.data, 0, 8);
+    msg.data[1] = station;
+
+    can2->write(msg);
+    
+    status._siriusChan = station;
+}
+
+void RadioEmulator::ParseCANMessage(CANMessage can_MsgRx)
+{
+    if (can_MsgRx.id == 0x000)
+    {
+        if (can_MsgRx.data[0] > 1)
+        {
+            radioOn = true;
+        }
+        else
+        {
+            radioOn = false;
+        }
+    }
+    
+    // this message seems to be a message requesting all other devices
+    // to start announcing their presence
+    if ((can_MsgRx.id >= 0x400) && (can_MsgRx.data[0] == 0xfd))
+    {
+//      SendOnMsg();
+    }
+    
+    if (can_MsgRx.id == 0x012)
+    {
+        if (memcmp(can_MsgRx.data, unlock, 6) == 0)
+        {
+        }
+        else if (memcmp(can_MsgRx.data, lock, 6) == 0)
+        {
+        }
+        else if (memcmp(can_MsgRx.data, trunk, 6) == 0)
+        {
+        }
+    }
+
+    if (can_MsgRx.id == 0x1bd)
+    {
+        // SDAR status
+        
+        if (status._siriusChan == 0)
+        {
+            status._siriusChan = can_MsgRx.data[1];
+        }
+
+        if (can_MsgRx.data[0] == 0x85)
+        {
+            ChangeSiriusStation(status._siriusChan, true);
+        }
+        
+        if (status._siriusChan != can_MsgRx.data[1])
+        {
+            ChangeSiriusStation(status._siriusChan, true);
+        }
+    }
+    
+    if (can_MsgRx.id == 0x3bd)
+    {
+        ReadSiriusText((char *)can_MsgRx.data);
+    }
+
+    if (can_MsgRx.id == 0x3a0)
+    {        
+        // note = 0x01
+        // volume up = 0x02
+        // volume down = 0x04
+        // up arrow = 0x08
+        // down arrow = 0x10
+        // right arrow = 0x20
+               
+
+        for ( int i = 0; i < 6; i++)
+        {
+            if (((can_MsgRx.data[0] & (0x01 << i)) == (0x01 << i)) && ((prevSWC & (0x01 << i)) == 0))
+            {
+                buttonTimeout.detach();
+                buttonClicks[i] ++;
+                buttonClickTimedOut = false;
+                buttonTimeout.attach_us(this, &RadioEmulator::ClickTimeout, 200 * 1000);
+            }
+            else if (((can_MsgRx.data[0] & (0x01 << i)) == 0) && ((prevSWC & (0x01 << i)) != 0))
+            {
+                buttonHeld[i] = false;
+                
+                if (buttonClickTimedOut == true)
+                {
+                    buttonClickTimedOut = false;
+                    buttonClicks[i] = 0;
+                }
+            }
+        }
+
+        prevSWC = can_MsgRx.data[0];
+    }
+
+
+    if (can_MsgRx.id == 0x000)
+    {
+        status._keyPosition = can_MsgRx.data[0];
+    }
+    else if (can_MsgRx.id == 0x002)
+    {
+        status._rpm = (can_MsgRx.data[0] << 8) + can_MsgRx.data[1];
+        status._speed = ((can_MsgRx.data[2] << 8) + can_MsgRx.data[3]) / 200;
+    }
+    else if (can_MsgRx.id == 0x003)
+    {
+        status._brake = can_MsgRx.data[3] & 0x01;
+        status._gear = can_MsgRx.data[4];
+    }
+    else if (can_MsgRx.id == 0x15)
+    {
+        status._batteryVoltage = (float)(can_MsgRx.data[1]) / 10;
+    }
+    else if (can_MsgRx.id == 0x01b)
+    {
+        // vin number
+        int part = can_MsgRx.data[0];
+        if (part < 3)
+        {
+            for (int i = 1; i < 8; i++)
+            {
+                status._vin[(part*8) + i-1] = can_MsgRx.data[i];
+            }
+        }
+    }
+    else if (can_MsgRx.id == 0x0d0)
+    {
+        if (can_MsgRx.data[0] == 0x80)
+        {
+            status._parkingBrake = true;
+        }
+        else
+        {
+            status._parkingBrake = false;
+        }                    
+    }
+    else if (can_MsgRx.id == 0x0EC)
+    {
+        if ((can_MsgRx.data[0] & 0x40) == 0x40)
+        {
+            status._fanRequested = true;
+        }
+        else
+        {
+            status._fanRequested = false;
+        }
+
+        if ((can_MsgRx.data[0] & 0x01) == 0x01)
+        {
+            status._fanOn = true;
+        }
+        else
+        {
+            status._fanOn = false;
+        }
+    }
+    else if (can_MsgRx.id == 0x159)
+    {
+        status._fuel = can_MsgRx.data[5];
+    }
+    else if (can_MsgRx.id == 0x1a2)
+    {
+        if ((can_MsgRx.data[0] & 0x80) == 0x80)
+        {
+            status._rearDefrost = true;
+        }
+        else
+        {
+            status._rearDefrost = false;
+        }
+
+        if ((can_MsgRx.data[0] & 0x40) == 0x40)
+        {
+            status._fanRequested = true;
+        }
+        else
+        {
+            status._fanRequested = false;
+        }
+
+        if ((can_MsgRx.data[0] & 0x01) == 0x01)
+        {
+            status._fanOn = true;
+        }
+        else
+        {
+            status._fanOn = false;
+        }
+    }
+    else if (can_MsgRx.id == 0x1c8)
+    {
+        status._headlights = can_MsgRx.data[0];
+    }
+    else if (can_MsgRx.id == 0x210)
+    {
+        status._dimmerMode = can_MsgRx.data[0];
+        if (can_MsgRx.data[0] == 0x03)
+        {
+            status._dimmer = -1;
+        }
+        else if (can_MsgRx.data[0] == 0x02)
+        {
+            status._dimmer = can_MsgRx.data[1];
+        }
+    }
+
+}
+
+void RadioEmulator::ReadSiriusText(char *data)
+{
+    int num = (data[0] & 0xF0) >> 4;
+    if (num > 7)
+    {
+        return;
+    }
+    
+    if ((data[0] & 0x01) == 1)
+    {
+        for (int i = 0; i < 8; i++)
+        {
+            memset(siriusText[num][i], 0, 8);
+        }
+    }
+    int part = (data[0] & 0x0E) >> 1;
+    if (part > 7)
+    {
+        return;
+    }
+    
+    memset(siriusText[num][part], 0, 8);
+    char tempString[8] = "";
+    memset(tempString,0,8);
+    
+    for (int i = 1; i < 8; i++)
+    {
+        tempString[i-1] = data[i];
+    }
+    
+    strncpy(siriusText[num][part],tempString,7);
+
+    int cls = (data[0] & 0x0F) >> 1;
+    if (cls - 1 == 0)
+    {
+        for (int i = 0; i < 8; i++)
+        {
+            memset(status._siriusTextLine[i], 0, 64);
+            for (int j = 0; j < 8; j++)
+            {
+                strcat(status._siriusTextLine[i], siriusText[i][j]);
+            }
+            
+//            usb->printf("%d: %s\r\n", i, status._siriusTextLine[i]);
+        }
+    }
+}
+
+void RadioEmulator::readCANbus(void)
+{
+    if (can2->read(can_MsgRx))
+    {
+        led3 = !led3;
+        needToParseCANMessage = true;
+        ReceivedCANMsg = true;
+    }
+    
+    if (needToParseCANMessage)
+    {           
+        needToParseCANMessage = false;   
+        
+        ParseCANMessage(can_MsgRx);
+    }
+}
+
+void RadioEmulator::HostComm(void)
+{               
+    if (usb->readable())
+    {
+        msg[serialCounter] = usb->getc();
+        serialCounter++;
+        
+        if (serialCounter == 4)
+        {
+            if ((msg[0] != 0x42) || (msg[1] != 0x42) || (msg[2] != 0x42) || (msg[3] != 0x42))
+            {
+                serialCounter = 0;
+                msg[0] = msg[1];
+                msg[1] = msg[2];
+                msg[2] = msg[3];
+            }
+        }
+    }
+
+    if (serialCounter > 13)
+    {
+        serialCounter = 0;
+        
+        if ((msg[0] == 0x42) && (msg[1] == 0x42) && (msg[2] == 0x42) && (msg[3] == 0x42))
+        {
+            ReceivedHostMsg = true;
+            
+            switch (msg[4])
+            {
+                case 0x00:
+                    opMode = slave;
+                break;
+                
+                case 0x01:
+                    status._volume = msg[5];
+                    status._balance = msg[6];
+                    status._fade = msg[7];
+                    status._bass = msg[8];
+                    status._mid = msg[9];
+                    status._treble = msg[10];
+                    
+//                    writeInitFile();
+                break;
+                
+                case 0x02:
+                    status._siriusChan = msg[5];
+                    ChangeSiriusStation(msg[5], false);
+                break;
+                
+                case 0x03:
+                    status._radioMode = (radioMode)msg[5];
+                    
+                    switch (status._radioMode)
+                    {
+                        case AM:
+                            status._amPreset = msg[6];
+                            status._amFreq = msg[7] + (msg[8] << 8);
+                        break;
+                        
+                        case FM:
+                            status._fmPreset = msg[6];
+                            status._fmFreq = msg[7] + (msg[8] << 8);
+                        break;
+                        
+                        case SAT:
+                            status._siriusPreset = msg[6];
+                            status._siriusChan = msg[7];
+                        break;
+                        
+                        case CD:
+                            status._cdNum = msg[6];
+                            status._cdTrackNum = msg[7];
+                            status._cdHours = msg[8];
+                            status._cdMinutes = msg[9];
+                            status._cdSeconds =  msg[10];
+                        break;
+                        
+                        case VES:
+                        break;
+                    }
+                break;
+                    
+            }
+
+        }
+    }
+}
+
+
+void RadioEmulator::ClickTimeout(void)
+{
+    for ( int i = 0; i < 6; i++)
+    {
+        buttonClickTimedOut = true;
+        buttonTimeout.detach();
+        if ((prevSWC & (0x01 << i)) == (0x01 << i))
+        {
+            buttonHeld[i] = true;
+        }
+        else
+        {
+            buttonHeld[i] = false;
+        }
+    }
+}
+
+void RadioEmulator::PowerUp(void)
+{
+    led1 = 1;
+    poweredDown = 0;
+    needToParseCANMessage = false;   
+    LPC_CAN2->BTR = 0x52001C;
+    *can_RS = 0;
+    ReceivedCANMsg = false;
+
+    canIRQ->rise(this, &RadioEmulator::StartEmulation);
+//    StartEmulation();
+}
+
+void RadioEmulator::StartEmulation(void)
+{
+    canIRQ->rise(0);
+    
+    CANBusTicker.attach(this, &RadioEmulator::WriteCANMessages, 0.5);
+
+    ChangeSiriusStation(status._siriusChan, true);
+    
+    CANTimeout.attach(this, &RadioEmulator::CheckCANTimeout, 1);
+}
+
+//------------------------------------------------------------------------------------------
+// These are all interrupts
+void RadioEmulator::RestartCAN(void)
+{
+    if (poweredDown == 5)
+    {
+        canIRQ->rise(NULL);
+        PowerUp();
+    }
+    poweredDown++;
+}
+
+void RadioEmulator::WriteCANMessages()
+{
+    if (radioOn)
+    {
+        led2 = !led2;
+        SendRadioModeMsg();
+        SendEVICMsg();
+        SendStereoSettingsMsg();
+    }
+}
+
+void RadioEmulator::CheckCANTimeout(void)
+{
+    if (!ReceivedCANMsg)
+    {
+        led1 = 0;
+        // Need to Power Down
+        CANBusTicker.detach();
+        CANTimeout.detach(); 
+                
+        *can_RS = 1;
+        
+        canIRQ->rise(this, &RadioEmulator::RestartCAN);
+    }
+    
+    ReceivedCANMsg = false;
+}
+
+void RadioEmulator::CheckHostTimeout(void)
+{
+    if (!ReceivedHostMsg)
+    {
+        led4 = 1;
+        opMode = standalone;
+    }
+    else
+    {
+        led4 = 0;
+    }
+
+    ReceivedHostMsg = false;
+}
+
+void RadioEmulator::SendStatusToHost(void)
+{
+    int size = sizeof(status);
+    
+    bool holding = false;    
+    if (buttonClickTimedOut == true)
+    {
+        // an action has occurred.... but are we still holding a button?
+        status.currentSWCAction = 0;
+        for (int i = 0; i < 6; i++)
+        {
+            status.currentSWCAction |= buttonClicks[i] << (2 * i);
+            if (buttonHeld[i] != true)
+            {
+                buttonClicks[i] = 0;
+            }
+            else
+            {
+                holding = true;
+            }
+        }
+    }
+    else
+    {
+        status.currentSWCAction = 0;
+    }
+    
+    static int okToSend = 0;
+    if (holding)
+    {
+        if (okToSend < 2)
+        {
+            status.currentSWCAction = 0;
+            okToSend++;
+        }
+        else
+        {
+            okToSend = 0;
+        }
+    }
+    else
+    {
+        okToSend = 0;
+    }
+    
+    status.SWCButtons = prevSWC;
+    
+    if (opMode == standalone)
+    {
+        if (status.currentSWCAction != 0)
+        {
+            if (buttonClicks[2] != 0)
+            {
+                if (status._volume > 0)
+                    status._volume --;
+            }
+            else if (buttonClicks[1] != 0)
+            {
+                if (status._volume < 40)
+                    status._volume ++;
+            }
+            else if (buttonClicks[4] != 0)
+            {
+                if (status._siriusChan > 0)
+                    ChangeSiriusStation(status._siriusChan-1, false);
+            }
+            else if (buttonClicks[3] != 0)
+            {
+                if (status._siriusChan < 256)
+                    ChangeSiriusStation(status._siriusChan+1, false);
+            }
+
+//            writeInitFile();
+        }
+    }
+    
+
+    RadioState tempStatus;
+    memcpy(&tempStatus, &status, size);
+    char *data = (char *)&tempStatus;
+ 
+    usb->putc(0x42);
+    usb->putc(0x42);
+    usb->putc(0x42);
+    usb->putc(0x42);
+
+    for (int i = 0; i < size; i++)
+    {
+        while(!usb->writeable())
+        {
+            wait_us(10);
+        }
+        
+        usb->putc(data[i]);
+    }
+
+}