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 . 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