ESPHome, CAN bus, and a Jeep Grand Cherokee 2012

Hello everyone,

Just doing a write up that may help anyone else wanting to do something similar as I did.

The Motivation

The long term goal was to monitor and control my car from anywhere. This would be achieved by either adding a hot-spot to the vehicle, or trying to find something similar to the Sim800L Component. Worst case, I could monitor while connected to a know WiFi network.

Chad Gibbons did a nice write up on how he connected to his vehicle. It was a similar vehicle as mine but unfortunately, none of the CAN codes matched :frowning:. This was detrimental since I was hoping to publish information to the EVIC of the vehicle and since I upgraded my audio head unit to an android, I have no way to find the CAN ID to do exactly that. However unfortunate this is, it gave me the final piece of motivation I needed to show it was possible.

The Setup

My vehicle has an internal CAN bus in addition to two CAN busses external to the cabin (connected to the ODB2 connector). The internal CAN bus is used for in cabin controls (steering wheel, audio amp, 120v inverter) but relays a lot of the states of the vehicle (transmission, throttle, speed. This CAN bus is used by the instrument cluster) and since at this point in time I wouldn’t be controlling much, I found it to be adequate.

Since my audio head unit was on the CAN bus, I tapped into the wires here and routed them under my passenger seat. I then connected the ESP32 along with a RaspberryPi. The reason I used both a Pi and ESP was the Pi had a lot of useful tools to read CAN codes (cansniffer). You can also tap into your ODB in the case that your vehicle doesn’t have a internal CAN bus.

I setup both the Pi and the ESP with wireguard so no matter what network I connect to, they always phone home.

CAN Bus Sniffing, ESPHome Config

Setup of:

binary_sensor:
sensor:
text_sensor:
spi:
canbus:
  on_frame:

After everything was connected, I was ready to start sniffing CAN bus codes. With the Pi, I installed can-utils and initiated cansniffer. This utility disregards repeating messages. I then started changing states (turning on blinker, opening doors, changing transmission gears) to read the CAN payloads. Here’s an example of the CAN setup for the binary_sensor of my doors:

#######################################################
###################### CAN setup ######################
#######################################################

spi:

  clk_pin: 13
  mosi_pin: 12
  miso_pin: 14
#  interface: hardware

canbus:

  - platform: mcp2515
#    clock: 8MHZ
    cs_pin: 27
    can_id: 0x295
#    bit_rate: 125kbps
    on_frame: 

###################### Door statuses ######################

# Doors

      - can_id: 0x244
        then:
          - lambda: |-
              if(x.size() > 0) {
                if(x[0] & 1) {
                  id(driverdoor).publish_state(true);
                  }
                if(!(x[0] & 1)) {
                  id(driverdoor).publish_state(false);
                  }


                if(x[0] & 2) {
                  id(passengerdoor).publish_state(true);
                  }
                if(!(x[0] & 2)) {
                  id(passengerdoor).publish_state(false);
                  }


                if(x[0] & 4) {
                  id(rightbackdoor).publish_state(true);
                  }
                if(!(x[0] & 4)) {
                  id(rightbackdoor).publish_state(false);
                  }


                if(x[0] & 8) {
                  id(leftbackdoor).publish_state(true);
                  }
                if(!(x[0] & 8)) {
                  id(leftbackdoor).publish_state(false);
                  }


                if(x[0] & 16) {
                  id(hatch).publish_state(true);
                  }
                if(!(x[0] & 16)) {
                  id(hatch).publish_state(false);
                  }


                if(x[0] & 32) {
                  id(rearwindow).publish_state(true);
                  }
                if(!(x[0] & 32)) {
                  id(rearwindow).publish_state(false);
                  }
              }

For my vehicle, I noticed that the manufacture like to used binary within the hex code which made it very simple for me setting up binary_sensor. For example, while watching cansniffer on the Pi when I opened my drivers door, CAN ID 0x244 updated x[0] changed to 0x01 and back to 0x00 when closed. Opening one door at a time, I was able to sniff all of them.

Finding the binary_sensor was pretty simple but what about a sensor like the throttle? This was a little more difficult to do but since it is still a user input, trivial to figure out.

With the car on but engine off, I pressed the throttle to the floor and read the value. I noticed CAN ID 0x292 updated x[2] to 0xFA, or 250 decimal, when floored and 0x00 when released. I made the assumption that it was linear relationship between the throttle being pressed and the value published.

I followed a similar process for the transmission gear, moving from Park to Drive. Interesting enough, the manufacture used the letters of the gear in the payload of CAN ID 0x20E payload x[4] with UTF-16 big endian as the character encoding. Park was 0x50 (P), Reverse was 0x52 (R), Neutral was 0x4E (N), and Drive was 0x44 (D).

Here’s the code for those:

#######################################################
###################### CAN setup ######################
#######################################################

spi:

  clk_pin: 13
  mosi_pin: 12
  miso_pin: 14
#  interface: hardware

canbus:

  - platform: mcp2515
#    clock: 8MHZ
    cs_pin: 27
    can_id: 0x295
#    bit_rate: 125kbps
    on_frame: 

###################### Throttle status ######################

      - can_id: 0x292
        then:
          - lambda: |-
              if(x.size() == 7) {
                float t;
                t = x[2] / 2.5;
                id(throttle).publish_state(t);
              }

###################### Transmission ######################

# Transmission gears

      - can_id: 0x20e
        then:
          - lambda: |-
              if(x.size() == 7) {
                if(x[4]==0x50) {
                  id(gear).publish_state("Park");
                  }
                if(x[4]==0x52) {
                  id(gear).publish_state("Reverse");
                  }
                if(x[4]==0x4e) {
                  id(gear).publish_state("Neutral");
                  }
                if(x[4]==0x44) {
                  id(gear).publish_state("Drive");
                  }
              }

# Terrain mode

      - can_id: 0x1e7
        then:
          - lambda: |-
              if(x.size() == 8) {
                if(x[3] & 1) {
                  id(terrain).publish_state("Sand");
                  }
                if(x[3] & 4) {
                  id(terrain).publish_state("Sport");
                  }
                if(x[4] & 5 and x[4] & 1) {
                  id(terrain).publish_state("Rock");
                  }
                if(x[3] & 16) {
                  id(terrain).publish_state("Auto");
                  }
                if(x[3] & 64) {
                  id(terrain).publish_state("Snow");
                  }
              }

Setup of:

switch:
script:
  then:
  - canbus.send:

Once I gained confidence, I then wanted to inject CAN messages to the vehicle for notifications. For example, if I left my garage door open, it would show on the vehicle that all my doors are open. However, the vehicle is constantly publishing the states of the doors so in order to over ride it, I would have to also be constantly sending the message. I achieved this by using a switch tied to a looping script. Since my vehicle ESP phones home over wireguard, Home Assistant can easily control it no matter where it is (granted, it’s connected to my hotspot).

The automation for Home Assistant is along the lines of if I am away from home and the garage door is open, publish this to the vehicle ESP.

Since I already found the codes for the doors, the script was relatively simple.

Here’s the code for that:

#####################################################
###################### Scripts ######################
#####################################################

script:

###################### Display CAN messages ######################

  - id: garageopen
    mode: single
    then:
    - while:
        condition:
          switch.is_on: garage_door_open_switch   
        then:
        - canbus.send: #Garage
            data: [ 0xff, 0x00, 0x00, 0x00, 0x80 ]
            can_id: 0x244
        - delay: 10ms

Eventually, I hope to tie into the EVIC of the instrument panel but as I mentioned, without the stock audio head unit, I have no way to find the CAN ID to do exactly that.

The Problems

As with everything, it wasn’t smooth sailing.

CAN bus shutdown:

I notice that if I have the ESP boot with the vehicle, about 5~minutes after, it shuts down the CAN bus; Meaning I can not see how fast the vehicle is going or anything on the instrument panel… Freaky to say the least when it first happened. Since this is the internal CAN bus, the vehicle functions as normal and can still safely be operated The car will then try to restart the CAN bus and has always successfully restarted it the first try. Not too sure why this happens but it doesn’t happen when the ESP has been on for a while.

Delay with updating states:

For the first few minutes of the ESP booting, takes a while to have the sensor update and even after being up for 15-20min, it seemed that the ESP got overwhelmed with the CAN bus codes on the bus.

Conclusion

Finding simple CAN messages for various sensor states of the vehicle is fairly straight forward but when getting to more complicated ones that require the vehicle to be on, it can be a mess sorting through all the data. Since I am entering this stage, I have noticed there are various websites that have manufactures CAN codes on them (for example, can2sky.com seems like a good website). I will be pursuing this next.

Any input would be great. I am hoping that this helps someone with their setup.

Feel free to ask any questions! I would love guidance or provide assistance.

Automate on!

DaviBoi

3 Likes

Dude this is amazing. I wish I could help here. I lack the guts to attempt a project like this. keep us updated!

This is awesome looking! How has your setup been working for you after a few months? Any improvements / changes that you’ve made? Any further issues with it interfering with the canbus? I’m looking to do something similar on my van, so I appreciate your work in this space.

I’ll post an update in the next couple of weeks. I have the ESP working rock solid to the point where I have it permanently installed and performing automations based on CAN bus sensor states

1 Like