Introduction: Car to Arduino Communication: CAN Bus Sniffing and Broadcasting With Arduino
From Wikipedia, the Controller Area Network (CAN) bus is a "vehicle bus standard designed to allow microcontrollers and devices to communicate with each other within a vehicle without a host computer." These devices can also be referred to as electronic control units (ECUs). Essentially the CAN bus is a bunch of linked ECUs within the vehicle that communicate with each based on a broadcast. Every ECU intercepts every broadcast, but individually decide whether or not to react to it.
Here's an example:
Let's imagine there's one ECU that controls the brake lights, one ECU that controls the car horn, and one ECU that controls the braking system. Whenever you blow the horn, the horn ECU sends a broadcast message out on the CAN bus network to every ECU it is connected to, including the brake light ECU and the braking system ECU. The brake light ECU intercepts that broadcast message, but chooses to ignore it because it has no relevance. The brake light ECU is really only waiting on the message from the brake system ECU. Also, the horn ECU doesn't react to the braking system ECU.
This broadcast system is broken down into different components; the two most important are message ID and message data.
For now, think of the message ID as an ECU address. The message data is the content. It is typically larger than the ID at around 8 bytes long.
Here's an example:
message ID: 620 data: 10 80 FF FF 80 20 00 80
The ECUs communicate with each other over a twisted wire pair holding CAN-high (CAN+) and CAN-low (CAN-). CAN-high and CAN-low are accessible through the OBD-II port under the steering wheel. This is how we'll get in!
Pro-tip: Use a wire tracer/tone generator to backtrace to other CAN Bus access points within your car.
Volkswagon has a good guide to how the CAN Bus network works: http://www.volkspage.net/technik/ssp/ssp/SSP_238.pdf
Step 1: Components and Assembly
Components:
1- Arduino UNO R3
2- Sparkfun (or other) CAN Bus Shield: https://www.sparkfun.com/products/10039
Note: Also available at SK Pang: http://skpang.co.uk/catalog/arduino-canbus-shield-with-usd-card-holder-p-706.html (SK Pang also supplies the needed CAN Bus library).
Note2: At the time of this writing, there were only 6 in stock at Sparkfun.
Note3: Sparkfun's CAN Bus shield also has a joystick (up, down, left, right, center), a micro SD slot, and support for GPS and LCD modules.
Note4: If you're feeling up to it, you can order the parts from Digikey and make your own using Sparkfun's provided EAGLE CAD drawing.
3- Wire pair or Sparkfun's OBD-II to DB9 cable:https://www.sparkfun.com/products/10087
Note: I found some old speaker wire that worked great.
4- breakable header pins - the CAN Bus shield doesn't include them: https://www.sparkfun.com/products/116
Assembly:
1- Break headers into 2x8 pin, 2x6 pin, and (optional - 1x4 pin sections)
2- Solder the headers to the CAN Bus shield.
Step 2: Familiarizing Yourself With the CAN Bus Library
Once assembled, be sure to download the CAN Bus Library for use with your Arduino IDE.
Library and Example files are located here:
https://github.com/sparkfun/SparkFun_CAN-Bus_Ardui...
Download link for Library and Examples:
https://github.com/sparkfun/SparkFun_CAN-Bus_Ardu...
- Library in the src/ folder
- Sparkfun (and my) examples are in the examples/ folder
CAN Bus Shield Initialization:
#include <Canbus.h> // don't forget to include these #include <defaults.h> #include <global.h> #include <mcp2515.h> #include <mcp2515_defs.h> void setup() { Serial.begin(9600); //Initialise MCP2515 CAN controller at the specified speed if(Canbus.init(CANSPEED_500)) Serial.println("CAN Init ok"); else Serial.println("Can't Init CAN"); delay(1000); }
Shield initialization will be required for all tasks. Here, we define our CAN bitrate and import our library. Every vehicle might use different bitrate speeds. For our example, we use 500 kbps.
Available options are:
CANSPEED_125 //CAN speed at 125 kbps
CANSPEED_250 //CAN speed at 250 kbps
CANSPEED_500 //CAN speed at 500 kbps
If you're unsure of your vehicle's CAN bitrate, do some Googling...
Read CAN Bus Messages:
We are reading every message here. It can be a bit overwhelming as you see the traffic flow through.
- ALL Messages
void loop() { tCAN message; if (mcp2515_check_message()) { if (mcp2515_get_message(&message)) { Serial.print("ID: "); Serial.print(message.id,HEX); Serial.print(", "); Serial.print("Data: "); for(int i=0;i<message.header.length;i++) { Serial.print(message.data[i],HEX); Serial.print(" "); } Serial.println(""); }} }
Filtering will cut out a huge chunk of noise. (You'll see what I mean when you begin to sniff unfiltered.)
- Filter Messages
void loop() { tCAN message; if (mcp2515_check_message()) { if (mcp2515_get_message(&message)) { if(message.id == 0x631) //filtering based on CAN bus message ID. { Serial.print("ID: "); Serial.print(message.id,HEX); Serial.print(", "); Serial.print("Data: "); for(int i=0;i<message.header.length;i++) { Serial.print(message.data[i],HEX); Serial.print(" "); } Serial.println(""); }}} }
message.header.length is the size of the CAN message.
The above was filtered by message ID. We can also filter based on message data.
if(message.id==0x631 and message.data[3]==0x04 and message.data[4]==0x0F)
Notes:
1- Messages can be longer than 3 digits.
2- We are formatting incoming message IDs and message data as HEX.
Write CAN Bus Messages:
In order to write a CAN Bus message, we need to first assemble the message components: message ID, message size, and message data. The message is broken down by message.id, message.header.rtr, message.header.length, and message.data[].
void loop() { tCAN message; message.id = 0x631; //formatted in HEX message.header.rtr = 0; message.header.length = 8; //formatted in DEC message.data[0] = 0x40; message.data[1] = 0x05; message.data[2] = 0x30; message.data[3] = 0xFF; //formatted in HEX message.data[4] = 0x00; message.data[5] = 0x40; message.data[6] = 0x00; message.data[7] = 0x00; mcp2515_bit_modify(CANCTRL, (1<<REQOP2)|(1<<REQOP1)|(1<<REQOP0), 0); mcp2515_send_message(&message); delay(1000); }
The message ID and data are written in HEX (0xFF, for example), which is the same format we read with.
mcp2515_send_message(&message); sends the message.
Step 3: Connect and Read / Write
The attached file, CAN_read_sample, is for simply reading all messages. I commented out filtering, so you should be able to modify it easily to include filtering of message ID and data.
I also attached a file, CAN_write_sample, for writing a message.
You have two options for connecting the Arduino to vehicle's CAN-high and CAN-low lines:
1- Hack up some speaker wire (or any wire pair) and connect the CAN-H and CAN-L through-holes on the shield to the OBD-II port.
CAN-H (shield) <-----> CAN-high (OBD-II)
CAN-L (shield) <-----> CAN-low (OBD-II)
2- Buy Sparkfun's OBD-II to DB9 Cable:https://www.sparkfun.com/products/10087. This also powers the Arduino through the car's 12v line. I haven't used it, but let me know how it works out... YMMV
Connect the Arduino to your car and computer, load the code, open the serial monitor, and watch the magic.
Step 4: What Next?
As you begin to read CAN bus messages, start manipulating your car.
- Unlock and lock the vehicle
- Pop the trunk
- Roll up and down windows
- Sounding the alarm
- Blow your horn
- Turn on and off your flashers
- Turn on and off your signal lights
- Turn of and off your lights and high beams
- Etc.
Remember that filtering is your friend!
See if you can find messages related to the above. Once you do, write the same messages back out through your Arduino using Step 2. See if you can unlock or lock your vehicle, pop the trunk, or blow your horn!
I hope to share my findings in the future!
Thanks for reading!
97 Comments
2 years ago
It is CRITICAL to add a CAN termination resistor of 120 Ohm that connects the CAN H, CAN L at the edge.
Reply 1 year ago
It depends on where on the Bus you are compared to the existing terminators. Try it with and without a terminator and better yet, check the resistance at your device to know what it is vs what it should be.
Reply 2 years ago
Critical as in the project won't work? Or critical as in your car will explode? :)
2 years ago
Hi, I hope you are all well.
Has anyone managed to work out how to write data to the CAN bus? I have the Sparkfun shield connected to an Uno and have connected to the OBD2 port on my car via the DB9 cable, and I can read messages but writing messages doesn't work. Any assistance would be greatly appreciated.
Reply 1 year ago
unsigned char stmp[8] = {0, 0, 0, 0, 0, 0, 0, 0};
void loop() {
// send data: CANid = 0x????????, FrameType(0=Standard,1=Extended), DataLength = 8, STMP(DataBuffer as 0x??)
//STMP data is HEX
stmp[0] = 0x6C; stmp[1] = 0x00; stmp[2] = 0x00; stmp[3] = 0x00;
stmp[4] = 0x00; stmp[5] = 0x66; stmp[6] = 0xC0; stmp[7] = 0x00;
CAN.sendMsgBuf(0x100082C, 1, 8, stmp);
stmp[0] = 0x6C; stmp[1] =
0x00; stmp[2] = 0x00; stmp[3] = 0x00; stmp[4] = 0x00; stmp[5] =
0x66; stmp[6] = 0xC0; stmp[7] = 0x00; CAN.sendMsgBuf(0x100082D, 1,
8, stmp);
stmp[0] = 0x00; stmp[1] = 0x00; stmp[2] = 0x00; stmp[3]
= 0x00; stmp[4] = 0x00; stmp[5] = 0x00; stmp[6] = 0x00; stmp[7] =
0x00; CAN.sendMsgBuf(0x110483C, 1, 8, stmp);
stmp[0] = 0x00;
stmp[1] = 0x00; stmp[2] = 0x3C; stmp[3] = 0x00; stmp[4] = 0x00;
stmp[5] = 0x00; stmp[6] = 0x00; stmp[7] = 0x00;
CAN.sendMsgBuf(0x1DE00020, 1, 8, stmp);
stmp[0] = 0x00; stmp[1] =
0x6C; stmp[2] = 0x3C; stmp[3] = 0x00; stmp[4] = 0x00; stmp[5] =
0x00; stmp[6] = 0x2C; stmp[7] = 0x64; CAN.sendMsgBuf(0x1A200020, 1,
8, stmp);
delay(1000); // send data per 1000ms
//SERIAL_PORT_MONITOR.println("Send ok");
}
Question 2 years ago
How can I read and send extended can frames. I have been trying to send message id - 18FEDF00, which gets converted to 700 when it is read. Can someone help me with respect to what changes should be made to the code above.
Answer 1 year ago
unsigned char stmp[8] = {0, 0, 0, 0, 0, 0, 0, 0};
void loop() {
// send data: CANid = 0x????????, FrameType(0=Standard,1=Extended), DataLength = 8, STMP(DataBuffer as 0x??)
//STMP data is HEX
stmp[0] = 0x6C; stmp[1] = 0x00; stmp[2] = 0x00; stmp[3] = 0x00; stmp[4] = 0x00; stmp[5] = 0x66; stmp[6] = 0xC0; stmp[7] = 0x00; CAN.sendMsgBuf(0x100082C, 1, 8, stmp);
stmp[0] = 0x6C; stmp[1] = 0x00; stmp[2] = 0x00; stmp[3] = 0x00; stmp[4] = 0x00; stmp[5] = 0x66; stmp[6] = 0xC0; stmp[7] = 0x00; CAN.sendMsgBuf(0x100082D, 1, 8, stmp);
stmp[0] = 0x00; stmp[1] = 0x00; stmp[2] = 0x00; stmp[3] = 0x00; stmp[4] = 0x00; stmp[5] = 0x00; stmp[6] = 0x00; stmp[7] = 0x00; CAN.sendMsgBuf(0x110483C, 1, 8, stmp);
stmp[0] = 0x00; stmp[1] = 0x00; stmp[2] = 0x3C; stmp[3] = 0x00; stmp[4] = 0x00; stmp[5] = 0x00; stmp[6] = 0x00; stmp[7] = 0x00; CAN.sendMsgBuf(0x1DE00020, 1, 8, stmp);
stmp[0] = 0x00; stmp[1] = 0x6C; stmp[2] = 0x3C; stmp[3] = 0x00; stmp[4] = 0x00; stmp[5] = 0x00; stmp[6] = 0x2C; stmp[7] = 0x64; CAN.sendMsgBuf(0x1A200020, 1, 8, stmp);
delay(1000); // send data per 1000ms
//SERIAL_PORT_MONITOR.println("Send ok");
}
2 years ago
Hello friends . Has anyone read the Renault Scenic 2? Can he send me life messages?
Question 4 years ago
I am trying to use can bus shield to read data from obd2 and then modify some of the data and then output the modified data to an external device. The external device is normally connected directly to the can bus plug so the data it receives is from the high and low cables. My question is once I modify the message via the processing the data in arduino and using my custom sketch how do I output it back out into the high and low cables from arduino output so that the external device receives the modified data in the hi / low format that the external device it’s expecting when it’s connected directly to the ond2 port.
Question 4 years ago
Hi. I am experimenting on a Suzuki Every minivan in order to decipher the message required for locking/unlocking the doors, using your (above, first instance) code for unfiltered reading. While I am able to initialize at 250, then passing to the void loop I simply cannot get past the first if condition: if (mcp2515_check_message()) as I keep on getting routed to the else condition I have added for simple tracking purposes. Any suggestions on what I need to modify on the code or in the libraries, in order to make it work? Thank you in advance.
Question 4 years ago
I'm working on an opensource project to read board computer data from busses (coaches). Those non-consumer vehicles have often a modified ODBII port. So we need to hook-up on the can-bus before that port. As cutting cables is not an option there are those 'Crocodile' adaptors (this device: ) to snif and read from the Can-H and Can-L cables
Have anyone of you here already experience here and if yes what hardware are you using to 'copy' the Can-H and Can-L cable data and then hook onto the Arduino?
I added 2 images. Both have the same function to snap over the 2 can cables to copy the data. 1 is the can-crocodile and the other is the clicq-system of Squarell. The later comes with a closed "non open-source" hardware system where you depend on the supplier.
Question 4 years ago on Introduction
We uploaded the code to the Arduino with the SparkfunShiels, the connection to the car is directly from Can-H, Can-L, GND to OBDII. We get initialization ok, but no data. We tried can-speed 500, 250, and 125. We are working on different cars and have tried the same procedure in different models. Our main goal is though to communicate with BMW E46 (we got information, that because of the K-line, the E46 needs to be connected to CAN via 14 and 8, in stead of 14 and 6 as the other models).
What to do to get the CAN-data?
Thanks in advance.
Answer 4 years ago
Hey, badillivanov
it looks like you're connecting your shield directly to an OBD port, most cars have a gateway between the various busses. Meaning you'll either need to request data from the bus through the gateway or uncover the bus wires and connect directly to them. You may want to check this guild out: https://www.instructables.com/id/How-to-Hack-and-Upgrade-Your-Car-Using-CAN-Bus/
cheers and good luck
Question 4 years ago
Hey everyone,
So, I've been trying to do a bit of can-bus hacking and sadly have been running into some problems.
First of all, when I connect the Sparkfun shield via OBD-|| cable I get no data, I figured this is due to the gateway not sending any data unless requested. Does anyone know how to request all data on all the busses?
Secondly, I have had trouble sending messages that give an expected outcome. e.g. I filtered out all the messages running on the dashboard bus, found the message for blinking my lights, but when I send the message back on the bus my lights don't turn on. How could I do this?
Any ideas are super welcome!
Thanks in advance
4 years ago
Hey can anyone help with my problem? I cant seem to get data. Tried debugging and got no where. I will attach pictures of the serial monitor and the hardware I have created.
4 years ago
Does anyone know if this code works with CAN BUS SHIELD V2.0 + Arduino MEGA 2560?
5 years ago
HI guys. I am starting to write a program about converting CAN data to USB. is there anyone who did that before? i would be wounder if you comment URL of some document about it.
All best
Reply 4 years ago
Could you explain what you mean exactly?
I've written code for seeing the can messages from the shield on the pc through the USB connection of the arduino. It also displays the messages in order.
Reply 5 years ago
Hello,
I'm trying also to do it; for the moment i have nothing except this :
https://advrider.com/f/threads/results-from-hackin...
but it is about a CAN of motorcycle;
If you have also some links please share it with me
Best regards.
5 years ago
Cant we just use the arduino uno or nodemcu directly to read the can messages. Instead of using the canbus shield?