First Project: Building CAN bus w/Controls to Install New (2015+) in Old (1996) Car

I've just started with an Arduino Uno V3 and a Playing With Fusion CAN bus Interface and a generic PCB shield for connecting resistors and input/output wires. I'm trying to adapt heated/cooled seats from a CAN bus vehicle into an older car.
I have 4 seat buttons (Driver Heat, Driver Cool, Passenger Heat, Passenger Cool) and there are 5 LED for both Driver and Passenger side. I'm using the Uno to read and count button presses (state ranges from 0-3, 0=off, 1=low heat/cool, 2=med heat/cool, 3=high heat/cool). Based on the state, different combinations of LED's in the switches will be HIGH, if state=0 then all LED are LOW.

Besides being more than a decade since I've really programmed or worked on any type of controller, the biggest problems I'm facing are:

  1. Pause or delay to prevent inadvertent button presses. It's real easy to get 2 presses when I only wanted 1.
  2. Canceling the opposite heat/cool when the other button is pressed. E.g., I want to change the driver's seat from hot to cold with a single button press instead of possibly having both hot and cold activated at the same time.
  3. Sniffing out the CAN bus messages from the original vehicle.
  4. Sending the correct CAN bus message for the selected switch-state.

I'll paste in my code as I go. I'm open to suggestions and am searching/reading/watching what I can. As you can see below, I only have the basics for turning on the 4 LEDs for a single switch. Good news is that the driver and passenger side switches don't interact. Just 2 switches working on each other to change the state.



// Pins for DH = Driver Heat, PH = Passenger Heat, DC = Driver Cool, PC = Passenger Cool
// swt = switch
int DH_swt = 0; 
int DC_swt = 1;
int DH_LED = 2; //resistors added between all LED pins and wires using proto-board
 int D_L1_LED = 3;
int D_L2_LED = 4;
int DC_LED = 5;
int D_L3_LED = 6;
int PH_swt = 7;
int PC_swt = 8;
int PH_LED = 9;
int P_L1_LED = 10;
int P_L2_LED = 11;
int PC_LED = 12;
int P_L3_LED = 13;

// variables to hold the switch states 
boolean newDHSwitchState = LOW;
boolean oldDHSwitchState = LOW;
boolean newDCSwitchState = LOW;
boolean oldDCSwitchState = LOW;
boolean newPHSwitchState = LOW;
boolean oldPHSwitchState = LOW;
boolean newPCSwitchState = LOW;
boolean oldPCSwitchState = LOW;

// setting the switch states to 4 at the start due to initial switch trigger during system start.  
// this starts all heat/cool in off condition.
byte DHswstate = 4;
byte DCswstate = 4;
byte PHswstate = 4;
byte PCswstate = 4;

// setting up the inputs/outputs and setting outputs to low
void setup()
{ 
  pinMode(DH_swt, INPUT_PULLUP);
  pinMode(DC_swt, INPUT_PULLUP);
  pinMode(DH_LED, OUTPUT);    digitalWrite(DH_LED, LOW);
  pinMode(DC_LED, OUTPUT);    digitalWrite(DC_LED, LOW);
  pinMode(D_L1_LED, OUTPUT);  digitalWrite(D_L1_LED, LOW);
  pinMode(D_L2_LED, OUTPUT);  digitalWrite(D_L2_LED, LOW);
  pinMode(D_L3_LED, OUTPUT);  digitalWrite(D_L3_LED, LOW);
  pinMode(PH_swt, INPUT_PULLUP);
  pinMode(PC_swt, INPUT_PULLUP);
  pinMode(PH_LED, OUTPUT);    digitalWrite(PH_LED, LOW);
  pinMode(PC_LED, OUTPUT);    digitalWrite(PC_LED, LOW);
  pinMode(P_L1_LED, OUTPUT);  digitalWrite(P_L1_LED, LOW);
  pinMode(P_L2_LED, OUTPUT);  digitalWrite(P_L2_LED, LOW);
  pinMode(P_L3_LED, OUTPUT);  digitalWrite(P_L3_LED, LOW);
 }


void loop()
{ 
  // Driver Side
  // Driver Heat Switch
   newDHSwitchState = digitalRead(DH_swt);
  if (newDHSwitchState != oldDHSwitchState)
  { 
    // has the switch been closed?
    if ( newDHSwitchState == HIGH )
    {
      // increments the value
      DHswstate++;
      if (DHswstate > 3) {
        DHswstate = 0;
      }
      // turn all of the LEDs off. 
      digitalWrite(DH_LED, LOW);
      digitalWrite(DC_LED, LOW);
      digitalWrite(D_L1_LED, LOW);
      digitalWrite(D_L2_LED, LOW);
      digitalWrite(D_L3_LED, LOW);
        // SEND CAN bus DH OFF Message here.

      // Turn on the DH indicator LED and PowerLevel 1 LED.
        if (DHswstate == 1) {
        digitalWrite(DH_LED, HIGH);
        digitalWrite(D_L1_LED, HIGH);
        digitalWrite(DC_LED, LOW);
        // SEND CAN bus DH Lvl 1 Message here.
        DCswstate = 0;
        delay(50);
      }
      if (DHswstate == 2) {
        digitalWrite(DH_LED, HIGH);
        digitalWrite(D_L1_LED, HIGH);
        digitalWrite(D_L2_LED, HIGH);
        digitalWrite(DC_LED, LOW);
        // SEND CAN bus DH Lvl 2 Message here.
        DCswstate = 0;
        delay(50);
      }
      if (DHswstate == 3) {
        digitalWrite(DH_LED, HIGH);
        digitalWrite(D_L1_LED, HIGH);
        digitalWrite(D_L2_LED, HIGH);
        digitalWrite(D_L3_LED, HIGH);
        digitalWrite(DC_LED, LOW);
        // SEND CAN bus DH Lvl 3 Message here.
        DCswstate = 0;
        delay(50);
      }
    }
    oldDHSwitchState = newDHSwitchState;
  }
// Driver Cool Switch
 newDCSwitchState = digitalRead(DC_swt);
  if (newDCSwitchState != oldDCSwitchState)
  { 
    // has the switch been closed?
    if ( newDCSwitchState == HIGH )
    {
      // increments the value
      DCswstate++;
      if (DCswstate > 3) {
        DCswstate = 0;
      }
      // Turn everything off.
      digitalWrite(DC_LED, LOW);
      digitalWrite(DH_LED, LOW);
      digitalWrite(D_L1_LED, LOW);
      digitalWrite(D_L2_LED, LOW);
      digitalWrite(D_L3_LED, LOW);
        // SEND CAN bus DC OFF Message here.
       
      // Turn on the DH indicator LED and PowerLevel 1 LED.
      if (DCswstate == 1) {
        digitalWrite(DC_LED, HIGH);
        digitalWrite(D_L1_LED, HIGH);
        digitalWrite(DH_LED, LOW);
        // SEND CAN bus DC Lvl 1 Message here.
        DHswstate = 0;
        delay(50);
      }
      if (DCswstate == 2) {
        digitalWrite(DC_LED, HIGH);
        digitalWrite(D_L1_LED, HIGH);
        digitalWrite(D_L2_LED, HIGH);
        digitalWrite(DH_LED, LOW);
        // SEND CAN bus DC Lvl 2 Message here.
        DHswstate = 0;
        delay(50);
      }
      if (DCswstate == 3) {
        digitalWrite(DC_LED, HIGH);
        digitalWrite(D_L1_LED, HIGH);
        digitalWrite(D_L2_LED, HIGH);
        digitalWrite(D_L3_LED, HIGH);
        digitalWrite(DH_LED, LOW);
        // SEND CAN bus DC Lvl 3 Message here.
        DHswstate = 0;
        delay(50);
      }
    }
    oldDCSwitchState = newDCSwitchState;
  }

// Passenger Side 
// Passenger Heat Switch
 newPHSwitchState = digitalRead(PH_swt);
  if (newPHSwitchState != oldPHSwitchState)
  { 
    // has the switch been closed?
    if ( newPHSwitchState == HIGH )
    {
      // increment the value of state
      PHswstate++;
      if (PHswstate > 3) {
        PHswstate = 0;
      }
      // turn everything off.
      digitalWrite(PH_LED, LOW);
      digitalWrite(PC_LED, LOW);
      digitalWrite(P_L1_LED, LOW);
      digitalWrite(P_L2_LED, LOW);
      digitalWrite(P_L3_LED, LOW);
        // SEND CAN bus PH OFF Message here.
      
      // Turn on the DH indicator LED and PowerLevel 1 LED.
      if (PHswstate == 1) {
        digitalWrite(PH_LED, HIGH);
        digitalWrite(P_L1_LED, HIGH);
        digitalWrite(PC_LED, LOW);
        // SEND CAN bus PH Lvl 1 Message here.
        PCswstate = 0;
        delay(50);
      }
      if (PHswstate == 2) {
        digitalWrite(PH_LED, HIGH);
        digitalWrite(P_L1_LED, HIGH);
        digitalWrite(P_L2_LED, HIGH);
        digitalWrite(PC_LED, LOW);
        // SEND CAN bus PH Lvl 2 Message here.
        PCswstate = 0;
        delay(50);
      }
      if (PHswstate == 3) {
        digitalWrite(PH_LED, HIGH);
        digitalWrite(P_L1_LED, HIGH);
        digitalWrite(P_L2_LED, HIGH);
        digitalWrite(P_L3_LED, HIGH);
        digitalWrite(PC_LED, LOW);
        // SEND CAN bus PH Lvl 3 Message here.
        PCswstate = 0;
        delay(50);
      }
    }
    oldPHSwitchState = newPHSwitchState;
  }
// Passenger Cool Switch
 newPCSwitchState = digitalRead(PC_swt);
  if (newPCSwitchState != oldPCSwitchState)
  { 
    // has the switch been closed?
    if ( newPCSwitchState == HIGH )
    {
      // incremement the value of state
      PCswstate++;
      if (PCswstate > 3) {
        PCswstate = 0;
      }
      // turn everything off.
      digitalWrite(PC_LED, LOW);
      digitalWrite(PH_LED, LOW);
      digitalWrite(P_L1_LED, LOW);
      digitalWrite(P_L2_LED, LOW);
      digitalWrite(P_L3_LED, LOW);
        // SEND CAN bus PC OFF Message here.

      
      // Turn on the DH indicator LED and PowerLevel 1 LED.
      if (PCswstate == 1) {
        digitalWrite(PC_LED, HIGH);
        digitalWrite(P_L1_LED, HIGH);
        digitalWrite(PH_LED, LOW);
        // SEND CAN bus PC Lvl 1 Message here.
        PHswstate = 0;
        delay(50);
      }
      if (PCswstate == 2) {
        digitalWrite(PC_LED, HIGH);
        digitalWrite(P_L1_LED, HIGH);
        digitalWrite(P_L2_LED, HIGH);
        digitalWrite(PH_LED, LOW);
        // SEND CAN bus PC Lvl 2 Message here.
        PHswstate = 0;
        delay(50);
      }
      if (PCswstate == 3) {
        digitalWrite(PC_LED, HIGH);
        digitalWrite(P_L1_LED, HIGH);
        digitalWrite(P_L2_LED, HIGH);
        digitalWrite(P_L3_LED, HIGH);
        digitalWrite(PH_LED, LOW);
        // SEND CAN bus PC Lvl 3 Message here.
        PHswstate = 0;
        delay(50);
      }
    }
    oldPCSwitchState = newPCSwitchState;
  }
}

2 Likes

Updated the code above. Now I have 4 switches controlling a total of 10 LED's. Once I have the correct CAN bus messages to send, I'll put those in for each of the incremented button presses/states.

I highly recommend looking up the bad things about automotive electrical systems such as reverse battery and load dump. You will need to protect from that or eventually your project will be a brick.

1 Like

Thank you. I’ll look at those. The can bus shield I selected uses a LM317MQ voltage regulator for automotive use. I’ll verify that it has appropriate protection.

Link to the CAN bus shield wiring diagram

Hello there, I happen to be working on an almost identical project. I am just now beginning to write the program. My first approach was to design a PCB that would replace the seat's ECU and to do away with the CAN BUS interface. However, if it's possible to emulate the CAN system, then that could be a lot cheaper.

Out of curiosity what did you take seats from and what car will you be placing them? What seat functions are you trying to preserve?

Seats are from a Ford Mustang. I’m trying to keep heating and cooling with the CAN bus. The memory option wasn’t available on the seats that I bought. I’m putting them in a classic truck that doesn’t have a CAN bus. I still need to record the bus traffic on a mustang. I’ve seen some information online about the mustang bus messages, but I haven’t found the needed seat traffic.

Actually, heating and cooling are very simple circuits within the seat modules. At least in mine. I'm working with 2010 Mercedes R230 multicontour seats (from SL 550) which are sophisticated enough but still likely very similar to yours. My seats use only a single IC S50080A to drive heating. The functions provided by it, which I would like to keep, are as follows:
-Switching of power to heating elements/pads (similar to a P-Mosfet)
-Load current detection
-Various built-in protections (overvoltage, thermal, short circuit)

Only the first is necessary to control heating, and the number of independent heating zones your seat will have determines how many devices are required. Different heating levels are achieved using PWM to adjust their duty cycles. The others are standard for the application for the sake of fault detection and resilience to the electrical environment found in automobiles. An older car will in most cases add to the importance of having these safety (of the electrical component) features.

As for cooling, the circuit is quite similar but with considerably less current requirement and also less potential to damage the seat by malfunctioning. The fans in your seat are probably powered by 12V DC and would be simple to control with really any switch. Here again, a mosfet could be controlled via PWM for different levels.

As for your code, it might a bit easier to manage and modify if you arrange your variables into arrays. This is especially true for comparing groups of variables to determine actions as you want to do with a single button. I have read that using a CAN bus shield, you can log all messages through an arduino. So long as you have a means of knowing which messages coincide with control inputs to the vehicle, you can try working through the logged entries and deducing which are related to the system of interest. This requires that you have the donor vehicle in operation.

Unfortunately I haven't found the online resources for codes either, Mercedes isn't very open to sharing this info and my seats are from a scrap yard. The components that make up the pneumatic controls in the R230 seat are quite a bit more expensive and limited in options.

1 Like

An example of using arrays, this is how I organize various I\O and variable for motor control:

/*          Multicontour Seat Controller (2011 Mercedes Benz R230)
 *           Arduino Mega 2560

 *                     Motor Reference Matrix:
 *                                       |___________CH1____________|___________CH2____________|
 *                                       |   M1   |   M2   |   M3   |   M4   |   M5   |   M6   |  
 *                                       |________|________|________|________|________|________|                                          ___
 *                         FORWARD/UP  A |   22   |   24   |   26   |   28   |   30   |   32   |    M[0]: Digital outputs to relays                                                         /   \
 *                          BACK/DOWN  B |___23___|___25___|___27___|___29___|___31___|___33___|                                                                                               |    \
 *                                       |   34   |   35   |   36   |   37   |   38   |   40   |    M[1]: Digital inputs from position sensors                          |    |
 *                                       |___34___|___35___|___36___|___37___|___38___|___40___| 
 *                                       |   A0   |   A0   |   A0   |   A1   |   A1   |   A1   |    M[2]: Analog inputs from switches
 *                                       |___A2___|___A2___|___A2___|___A3___|___A3___|___A3___|   
 *                                       |   70   |  170   |  425   |   70   |  170   |  425   |    M[3]: Nominal analog values for switches
 *                                       |___70___|__170___|__425___|___70___|__170___|__425___|
 *                       
 * 
 *                     Positional Data Matrix:
 *                                       |___M1___|___M2___|___M3___|___M4___|___M5___|___M6___|
 *                                       |                                                           [0]: Continuously incremented during adjustment, stored in EEPROM if different from start-up
 *                                       | position[0]: current displacement                                                                  
 *     All values relative to full       | position[1]: saved position 1                             [1-3]: Stored in EEPROM, overwritten if button is held for 3 seconds.  
 *     -forward adjustment of seat.      | position[2]: saved position 2       
 *           ---------------             | position[3]: saved position 3                             [4]: Written upon each adjustment
 *      Position values increase from    | position[4]: previous position before selection
 *         0 at forward position         | position[5]: start-up position                            [5]: Written once at start-up
 */
1 Like

Ford has motion control as controlled by seat mounted switches. Just need 12v and a ground to enable full motion.

Heating and cooling is controlled via a computer under the passenger seat. The CAN bus messages are sent from the dash buttons and these trigger different heating or cooling levels for each seat. I’m probably going to have to find a used car for sale and take it for a test drive. Then read CAN bus messages while I have it for 30 minutes or so.

Have you tried powering the seats out of the car and adjusting them? Aside from commands sent from other modules, they may be programmed to enter a 'standby' mode in the absence of certain signals on the bus. I would imagine that without technical guidance, tracking any automated messages would not be a small job. I don't want to discourage you however, I am very interested to see what you find. Do you have any experience or background working with CAN?

Yes. I hooked them up to a battery and all directional adjustments worked fine. I purchased a wiring diagram for the car just to verify where everything connects. I built a cable to connect both chairs to the climate controller computer mounted under the passenger seat.

From what I’ve found online, there is a CAN bus message to wake up the unit. I’m not very experienced with the CAN bus, but I’ve been reading what I can find for it. It looks relatively straightforward. I just need something to listen to.

For my code, I need to be able to send that wake up message at a regular interval to keep the temperature control computer awake.

Well, It seems you're well on the right track. Good luck on the CAN side of things and if you don't mind, please post your progress. It sounds like we're stuck at more or less the same place.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.