Dodge/Chrysler Radio Emulator Connect to Interior CAN bus

Dependencies:   mbed SDFileSystem

radioEmulator.cpp

Committer:
rtgree01
Date:
2011-01-31
Revision:
0:9bc41d70bdd3

File content as of revision 0:9bc41d70bdd3:

#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]);
    }

}