Some stuff from my 14 charger

this comes from hours of testing but here it is. once I was “on the CANBUS” it was just a matter of hitting buttons off/on
Steering wheel buttons
318#00000000100CFF00 #up arrow left side steering wheel
318#00000000040CFF00 #down arrow left side steering wheel
318#00000000010CFF00 # back button left side steering wheel
318#00000000400CFF00 #forward button left side
318#00000010000CFF00 #behind steering wheel skip track
318#00000020000CFF00 #behind steering wheel rewind
318#00000004000CFF00 #volume up right side behind steering wheel
318#00000008000CFF00 #volume down right side behind steering wheel
318#00000001000CFF00 #change input behind steering wheel right side
318#00000040000CFF00 #behind steering wheel middle button seek radio

Shifter I have the 5-speed V6 I can always connect to the 8-speed and read the data, but it looks like it just increments the 4th number
332#18507840A9F900FF #park
332#18527940AFF900FF #reverse
332#184E7A40B1F900FF #neutral
332#184E7A40B1F900FF #drive
332#18317C40ADF900FF #manual mode 1 gear
332#18327C40AFF900FF #manual mode 2 gear
332#18337C40AFF900FF #manual mode 3 gear
332#18347C40AFF900FF #manual mode 4 gear
332#18357C40AFF900FF #manual mode 5 gear

Sport mode button touch screen this was from my radio the 4.3" (I used a auto tazer to activate sport mode on my V6) ((will log that code soon once I get a splitter)), but the light came on/off in the cluster with the command below.
2FA#0001000800000000 #off
2FA#0001004800000000 #on

I need a bit of help creating a sketch that will listen for a certain ID and then send my own. if anyone can do that it would be appreciated. i think these should be good for 2011-14 dodge chargers.

1 Like

Cool stuff! I wonder if the same IDs are used on a 2015 RAM 1500 … time to find out.

If you know the ID you are listening for, you can set it up in the CAN filters so you can quickly find what you are looking for without having to process all the messages. It should be in the Particle docs how to set the filters.

Someone else was doing something similar; posts figuring out how to send a message are here:
https://community.carloop.io/t/sending-commands/288/19

nice appreciate it, I know the ID I’m looking for. basically I’m trying to add a custom function on a button press. for example if I press the back button 3 times in a row on my steering wheel buttons it will capture that input and send the ID I want it to send. I know its something I’m overlooking I’m just in a mess of open tabs and doc spread across my desk lol I know ill get it soon enough.

more stuff from my 2014 charger

Push button start
122#04020000 #run mode
122#03020000 #acc mode
122#00010000 #off

Vin number
3E0#

Traction control
302#02F0000000000000 #turn off and on same command

**AC **
342#00010A0000 #turn ac up
342#00010B0000 #turn ac down
34E#7F007F000148007F # AC on
34E#7F7F7F7F000F007F #AC off
34E#7F007F000448007F #max ac off
34E#7F007F000748007F #max ac on
342#0000020000 #turn temp down
342#0000010000 #turn temp up
342#0900000000 #mode face
342#0700000000 #feet face
342#0500000000 #feet
342#0300000000 #defrost & feet
342#0100000000 #front defrost send to turn off/on
342#0000002000 #rear defrost send to turn off/on
342#0000400000 #climate off/on
342#00000B0000 #fan speed down
342#00000A0000 #fan speed up

Defrost button
30E#A4EE1DFF07FF07FF #off
30E#94EE1DFF07FF07FF #on
30E#84EE1DFF07FF07FF

Circulation button ac
342#0000000200 # send to turn off & on

Door locks
2E2#0201 #unlock
2E2#0401 #lock
2E2#2001 #lock windows button down
2E2#0001 #lock windows button up

Radio off (still trying to figure this one out but I think this is what is being sent) (mark this one as unsure) someone else hopefully can figure this one out. I keep sending the command but I can’t get it to turn off/on in thinking I’m missing an ID somewhere.
328#40410046004D0020
328#300100390037002E
328#2001003900200050
328#1001003400200020
328#0001002000000000
328#0000000000000000

windows

I can only control the passenger window, I’m still looking into why

reading through the docs looks like I can use “Interrupts” to get the function I’m looking for.

I am not sure how you could set an interrupt to trigger on something like that.
(Usually interrupts are triggered on a GPIO or a timer.)
More likely it would be better to set a filter(s) so your code only sees the messages you are interested in. Have your code continuously poll the messages that come through the filters. When you get a message you want, use a counter or a trigger to keep track of it. Do some sort of debugging, such as a serial message or flashing the LED on D7 to let you know you are getting the messages.

Once that part works, then your code can send out a message when there is a trigger or when the counter gets to the desired value.

oh man appreciate the direction! ill post the sample code soon.

found this link online it seems like its for the CANBUS triple. but I’m sure its a step in the right direction.
https://www.reddit.com/r/CANBus/comments/4y2n1j/can_i_pay_somebody_to_write_me_a_canbus_triple_app/

I’ve managed to make my own GUI (really simple one at the moment) but it works, I’m almost ready to replace my screen with one of my own.
I have my setup working now and I can connect to it through the hotspot I’ve setup and use ssh/VNC to login and do what I want, once I add the screen then I can make the GUI look nice and function like it should.

This sounds more and more interesting with every new development!

I had someone share some code from their project I’m hoping someone can use this too maybe port it the the carloop

#include <LXInterface.h>
#include <LXState.h>

Thread thread;
Timer t;

LXInterface::LXInterface(PinName tx, PinName rx) :
CANDevice(tx, rx),
m_displayed_position(0),
m_displayed_length(0),
m_update_volume(false)
{
printf(“LXInterface Initializing\r\n”);

memset(&status, 0, sizeof(status));

m_muted = false;

m_HU._volume = 10;
m_HU._bass = 10;
m_HU._mid = 15;
m_HU._treble = 15;
m_HU._balance = 10;
m_HU._fade = 10;

timed = false;
SWITimerRunning = false;
prevSWC = 0;
SWITickCount = 0;

PowerUp();
printf(“LXInterface initialized\n\r”);

strcpy(m_displayed_text, “”);
}

void LXInterface::PowerUp(void)
{
ReceivedCANMsg = false;
LPC_CAN2->BTR = 0x52001C;

writeCANFlag = 0;

thread.start(callback(Operate, this));
}

void LXInterface::MuteAction()
{
if (m_muted)
{
SetVolume(m_unmuted_volume);
}
else
{
SetVolume(0);
}
}

void LXInterface::SetVolume(char new_volume)
{
m_HU._volume = new_volume;

m_update_volume = true;

// trigger the new volume setting and display
}

char LXInterface::GetVolume()
{
return(m_HU._volume);
}

void LXInterface::Operate(void const argument)
{
LXInterface
lx = (LXInterface*)argument;
t.start();

while (1)
{
lx->writeCANFlag++;

  if (lx->writeCANFlag == 1000)
  {
     lx->writeCANFlag = 0;
     lx->SendSiriusMessage();
  }

  if (((lx->writeCANFlag % 100) == 0) || (lx->m_update_volume == true))
  {
     lx->SendEVICMsg();
     lx->SendStereoSettingsMsg();

     lx->m_update_volume = false;
  }

  if ((lx->writeCANFlag % 50) == 0)
  {
     lx->SendRadioModeMsg();
  }

  if ((lx->writeCANFlag % 50) == 0)
  {
     lx->UpdateText();
  }

  lx->readCANbus();

  Thread::wait(1);

}
}

void LXInterface::InternalSWI(void const argument)
{
LXInterface
lx = (LXInterface*)argument;
lx->m_HU.SWCAction = 0;

if (!lx->SWITimerRunning && (lx->m_HU.SWCButtons != 0))
{
lx->SWITickCount = 0;
lx->stacksize = 0;
lx->SWITimerRunning = true;
}

if (lx->SWITimerRunning)
{
if (lx->timed)
{
lx->SWITickCount++;
}

  if (((lx->m_HU.SWCButtons == 0) && (lx->prevSWC != 0)) || ((lx->m_HU.SWCButtons != 0) && (lx->m_HU.SWCButtons != lx->prevSWC)))
  {
     // either a button was just pressed, or just released... time to do something
     lx->buttonstack[lx->stacksize] = lx->m_HU.SWCButtons;
     lx->timestack[lx->stacksize] = lx->SWITickCount;
     lx->stacksize++;
  }

// printf(“%d %d %x %x %x\r\n”, lx->stacksize, lx->SWITickCount, lx->m_HU.SWCButtons, lx->timed ? 1 : 0, lx->timestack[1]);

// printf(“Stack = %d\r\n”, stacksize);
if (lx->stacksize == 6)
{
// triple click?
lx->m_HU.SWCAction = 0x80000000 + (lx->buttonstack[0] << 5) + kTripleClick;

     // reset everything because pressed and released 3 times.... this stops the triple hold
     lx->SWITimerRunning = false;
     lx->stacksize = 0;
     lx->SWITickCount = 0;
  }
  else if (lx->stacksize == 5)
  {
     if (lx->SWITickCount >= 2)
     {
        // triple click and held
        lx->m_HU.SWCAction = 0x80000000 + (lx->buttonstack[0] << 5) + kTripleHeld;
     }
  }
  else if (lx->stacksize == 4)
  {
     // double click?
     if (lx->timestack[3] > 2)
     {
        // reset everything because double click and held was released
        lx->SWITimerRunning = false;
        lx->stacksize = 0;
        lx->SWITickCount = 0;
     }
     else if (lx->SWITickCount >= 4)
     {
        lx->m_HU.SWCAction = 0x80000000 + (lx->buttonstack[0] << 5) + kDoubleClick;
        // reset everything because double click in < 600 ms
        lx->SWITimerRunning = false;
        lx->stacksize = 0;
        lx->SWITickCount = 0;
     }
  }
  else if (lx->stacksize == 3)
  {
     if (lx->SWITickCount >= 2)
     {
        // double click and held
        lx->m_HU.SWCAction = 0x80000000 + (lx->buttonstack[0] << 5) + kDoubleHeld;
     }
  }
  else if (lx->stacksize == 2)
  {
     // single click
     if (lx->timestack[1] > 2)
     {
        // reset everything because single click and held was released
        lx->SWITimerRunning = false;
        lx->stacksize = 0;
        lx->SWITickCount = 0;
     }
     else if (lx->SWITickCount >= 2)
     {
        lx->m_HU.SWCAction = 0x80000000 + (lx->buttonstack[0] << 5) + kSingleClick;
        // reset everything because only a single click
        lx->SWITimerRunning = false;
        lx->stacksize = 0;
        lx->SWITickCount = 0;
     }
  }
  else if (lx->stacksize == 1)
  {
     if (lx->SWITickCount >= 2)
     {
        // single click and hold
        lx->m_HU.SWCAction = 0x80000000 + (lx->buttonstack[0] << 5) + kSingleHeld;
     }
  }

  if (!lx->SWITimerRunning)
  {
     lx->queue.cancel(lx->m_swi_event);
  }

}

lx->prevSWC = lx->m_HU.SWCButtons;

// start timer at first press…
// if released, and no press in < 250 ms… single click
// if released, pressed in < 250 ms, and not released in < 500… then single click and hold
// if released, pressed in < 250 ms, and released again in < 500 ms… then double click
// if released, pressed in < 250 ms, released, then pressed and not released < 500 ms… then double click and hold
// if released, pressed in < 250 ms, released, then pressed and released < 500 ms… then triple click
}

void LXInterface::SendOnMsg()
{
static CANMessage msg;
msg.id = 0x416;
msg.len = 8;
static char temp[8] = {0xfd, 0x16, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff};
memcpy(msg.data, temp, 8);
CANDevice.write(msg);
}

void LXInterface::SendRadioModeMsg()
{
static CANMessage msg;
msg.id = 0x09F;
msg.len = 8;

msg.data[7] = 0x0f;
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;

msg.data[0] |= 0x04;
msg.data[1] = 0;
msg.data[2] = m_HU._volume; //sirius_status.channel;
// msg.data[2] = 2;
CANDevice.write(msg);
}

void LXInterface::SetText(const char* text)
{
memset(m_displayed_text, 0, 128);
m_displayed_length = strlen(text);// >= 64 ? 64 : (strlen(text) % 64);
m_displayed_position = 0;
strncpy(m_displayed_text, text, m_displayed_length);
}

void LXInterface::UpdateText()
{
static int count = 0;
if (count == 0)
{
if (strlen(m_displayed_text) != 0)
{
SetEVICText(m_displayed_text, m_displayed_position, m_displayed_length - m_displayed_position > 8 ? 8 : m_displayed_length - m_displayed_position);
if (m_displayed_position + 8 >= m_displayed_length)
{
m_displayed_position = 0;
}
else
{
m_displayed_position += 8;
}
}
}
count++;
if (count > 20)
{
count = 0;
}
}

void LXInterface::SendEVICMsg()
{
static CANMessage msg;
msg.id = 0x394;
msg.len = 6;

memset(msg.data, 0, 8);

msg.data[0] = 0x61;
msg.data[1] = 0x03;
msg.data[2] = 0xDB;

CANDevice.write(msg);
}

void LXInterface::SendStereoSettingsMsg()
{
static CANMessage msg;
msg.id = 0x3D0;
msg.len = 7;

msg.data[0] = m_HU._volume;
msg.data[1] = m_HU._balance;
msg.data[2] = m_HU._fade;
msg.data[3] = m_HU._bass;
msg.data[4] = m_HU._mid;
msg.data[5] = m_HU._treble;

CANDevice.write(msg);
}

void LXInterface::SetEVICText(const char *text, const int start_index, const int length)
{
static CANMessage msg;
msg.id = 0x1BF;
msg.len = 8;
memset(msg.data, 0, 8);

memcpy(msg.data, text + start_index, length);
// printf(“EVIC Text = %s\r\n”, msg.data);

CANDevice.write(msg);
}

void LXInterface::SendSiriusMessage()
{
static CANMessage msg;
msg.id = 0x1bd;
msg.len = 4;
msg.data[0] = 0x09;
msg.data[1] = m_HU._volume;
msg.data[2] = 0x6D;
msg.data[3] = 0x83;
CANDevice.write(msg);

msg.id = 0x1be;
msg.len = 8;
msg.data[0] = ‘R’;
msg.data[1] = ‘O’;
msg.data[2] = ‘C’;
msg.data[3] = ‘K’;
msg.data[4] = 0;
msg.data[5] = 0;
msg.data[6] = 0;
msg.data[7] = 0;
CANDevice.write(msg);

msg.id = 0x3b0;
msg.len = 6;
msg.data[0] = 23;
msg.data[1] = m_HU._volume;
msg.data[2] = 0;
msg.data[3] = 0;
msg.data[4] = 0;
msg.data[5] = 0;
msg.data[6] = 0;
msg.data[7] = 0;
CANDevice.write(msg);
}

void LXInterface::ParseCANMessage(CANMessage can_MsgRx)
{
// 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))
{}

if (can_MsgRx.id == 0x000)
{
/*
if (can_MsgRx.data[0] > 1)
{
radioOn = true;
}
else
{
radioOn = false;
}
*/
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]) >> 7;

  // what are the other 4 bytes?

}
else if (can_MsgRx.id == 0x003)
{
status._brake = can_MsgRx.data[3] & 0x01;
status._gear = can_MsgRx.data[4];
}
else if (can_MsgRx.id == 0x012)
{}
else if (can_MsgRx.id == 0x14)
{
status._odometer = (can_MsgRx.data[0] << 16) + (can_MsgRx.data[1] << 8) + can_MsgRx.data[2];
// what are the other 4 bytes?
}
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 >= 0) && (part < 3))
{
for (int i = 1; i < 8; i++)
{
status._vin[(part*7) + 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 == 0x1bd)
{
// SDAR status

  // if (sirius_status_channel == 0)
  // {
  //    sirius_status_channel = can_MsgRx.data[1];
  // }
  //
  // if (can_MsgRx.data[0] == 0x85)
  // {
  //    ChangeSiriusStation(sirius_status_channel, true);
  // }
  //
  // m_HU._siriusChan = can_MsgRx.data[1];

}
else if (can_MsgRx.id == 0x1bf)
{
for (int i = 0; i < 8; i++)
{
printf(“%02x “, can_MsgRx.data[i]);
}
printf(”\r\n”);
}
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 = 255;
}
else if (can_MsgRx.data[0] == 0x02)
{
status._dimmer = can_MsgRx.data[1];
}
}
else if (can_MsgRx.id == 0x3a0)
{
// note = 0x01
// volume up = 0x02
// volume down = 0x04
// up arrow = 0x08
// down arrow = 0x10
// right arrow = 0x20

  // if (can_MsgRx.data[0] != m_HU.SWCButtons)
  // {
  //    printf("can: swi data = %x\r\n", can_MsgRx.data[0]);
  //    m_HU.SWCButtons = can_MsgRx.data[0];
  //    timed = false;
  //    printf("timed = %x\r\n", timed ? 1 : 0);
  //    InternalSWI(this);
  //    timed = true;
  //    printf("timed = %x\r\n", timed ? 1 : 0);
  //    if (!SWITimerRunning)
  //    {
  //       m_swi_event = queue.call_every(200, InternalSWI, this);
  //    }
  // }


  if (can_MsgRx.data[0] != m_HU.SWCButtons)
  {
     if (can_MsgRx.data[0] == 0)
     {
        // just released....
        m_HU.SWCAction = 0x80000000 + (m_HU.SWCButtons << 5) + kSingleClick;
     }
  }
  else if (can_MsgRx.data[0] != 0)
  {
     // held
     m_HU.SWCAction = 0x80000000 + (m_HU.SWCButtons << 5) + kSingleHeld;
  }

  m_HU.SWCButtons = can_MsgRx.data[0];

}
/*
else if (can_MsgRx.id == 0x3bd)
{
ReadSiriusText((char *)can_MsgRx.data);
}
*/
}

/*
void LXInterface::ReadSiriusText(char *data)
{
int num = (data[0] & 0xF0) >> 4;
if ((num > 7) || (num < 0))
{
return;
}

int part = (data[0] & 0x0E) >> 1;
if ((part > 7) || (part < 0))
{
    return;
}

if ((data[0] & 0x01) != 0)
{
    memset(sirius_status.text[num], 0, 64);
}

memset(&sirius_status.text[num][(part * 7)], 0, 7);

for (int i = 1; i < 8; i++)
{
    sirius_status.text[num][(part * 7) + (i-1)] = data[i];
}

}
*/

void LXInterface::readCANbus(void)
{
if (CANDevice.read(can_MsgRx))
{
ReceivedCANMsg = true;
ParseCANMessage(can_MsgRx);
}
}

@dubb45, Do you have the code in Github, in a Particle share or something similar to share what progress you have achieved?
What error messages are you getting when you try to compile?

I can help out a bit, but the main thing will be to get appropriately organized to make it easier for the elites on the Particle community to help support the porting activity.

heres a github link to the files. DodgeRadioLib