Yes We CAN BUS With Arduino in 30 Seconds!

84,761

102

23

Introduction: Yes We CAN BUS With Arduino in 30 Seconds!

About: We make because why not!

Hello Arduinos!

This Instructable is trying to summarize what I ended up with after a long time of search, tutorials, trials and datasheets to build a functional CAN BUS node. I will try to keep it as easy and concise as possible to get you straight to a properly working setup, saving your time for further developments afterwards.

The first 3 steps are the basic ones that will initiate a CAN BUS at your home, the rest of the steps are a little bit advanced and real-life CAN situations.

CAN BUS is a two-wire, half-duplex communication protocol that is widely used in Automotive industry. One of its greatest advantages is that it connects any number of ECUs (or microcontrollers) in your car through the two-wire bus, CAN High and CAN Low, reducing the weight of wires that could be gained by using point-to-point communication between ECUs.

Enough talking and let's grease our hands!


You can continue reading about CAN from wiki as it really gives a very good and sufficient introduction to the topic.

Step 1: Build the Hardware

We could buy any of the plug-and-play Arduino CAN shields, but building the hardware ourselves is easy, more fun and cost reduction, bro.

What you need to build one node:

  • x1 Breadboard.
  • x1 MCP2515 Microchip CAN Controller.
  • x1 MCP2551 Microchip CAN Transceiver.
  • x1 20KΩ Resistor.
  • x1 10KΩ Resistor.
  • x1 100Ω Resistor.
  • x1 16 MHz Crystal Oscillator.
  • x2 27 pF Capacitors.
  • x3 LEDs.
  • x3 220Ω Resistors.

Schematic key:

CAN H: CAN High and is connected to the CAN High wire of the bus.

CAN L: CAN Low and is connected to the CAN Low wire of the bus.

VCC: 5V power source from the Arduino.

GND: connected to Arduino's ground pin.

UNO X: as X is an integer, means connect this pin to Arduino's digital pin X.

Here, our setup is using 3 means of communication protocols:

1) UART: to talk to your computer's Serial monitor.

2) SPI: to talk to the CAN controller.

3) CAN: to talk to other neighbours in the bus.

Wire up your components to the board according to the schematics attached above and let's move to the next step.

Step 2: Download and Install the CAN Library

In this Instructable, I'm using Seeed Studio open-source CAN Library which you can download from their github as shown above.

Keep the downloaded file as zipped as it is (because Arduino likes this) and add the library to Arduino as shown above, as well.

By this point you are ready to move to the next step to combine both software and hardware and try your Hello CAN examples :D

I'm currently using the latest Arduino (1.8.3) but it works with the old versions as well.

Step 3: Upload Your First Code

The codes attached are basically the examples of the library itself but with a gentle touch of simplicity. I think that both files are over-commented, but feel free to ask about any bit of code you find unclear, because when you solid understand this basic example, you can follow up with the next few steps and even dig deeper and tweak the codes as you like.

The file called Send packs 8 characters (8 bytes) into a message of the ID 0xF1, because I'm an F1 fan, and puts it on the bus.

The file called Receive keeps polling the CAN receive buffers until any message comes in. It then breaks the incoming data into an ID, data length and the data itself.

Yes, You CAN BUS now!

Step 4: CAN Is a Message-Based Protocol!

CAN is a message-based protocol, which means that the messages and their content are more important than the sender ECU itself. So, the ECUs aren't given IDs, but each message has a unique ID in a specific bus. These IDs are responsible for setting the priority of messages in case of two or more ECUs are trying to put their messages on the bus. The LOWER decimal value ID has HIGHER priority.

Given that, the message with ID = 0x05 has more priority than our beloved message of the previous example with ID = 0xF1.

A Big Example:

If we consider ourselves in a real car, we might assume that the message that informs a fatal problem in the engine will be given the highest ever priority (Logical, no?). So, whatever ECU tries to send this message will win the bus and continue sending its message while everyone else is just listening until it finishes.

At any time, every CAN BUS node sees the message being sent through the bus. But not all of them read it and send it to their ECUs. That's because in our example the rear wing ECU or the front-right headlights ECU don't care at all about a problem in the engine, so they see the message that contains engine failure and ignore it. On the other hand, they are the only ECUs who read the messages like: retract the wing or shut the headlights.

And here it comes the idea of Message Filtering that lets an ECU reads only the messages useful to it and ignore everything else. And since our code allows us to know the ID of the message, we can easily apply filtering.

The example file attached here adds only one if statement to the basic Receive file to read only the messages with ID = 0xF2. Let this new code receive from the basic Send code and it will print nothing.

Step 5: Extract Useful Signals From a CAN Message

By reaching this line now we:

  1. Wired up the hardware circuit.
  2. Sent and received CAN messages.
  3. Filtered the stream of messages and read only those that interest us.

But all we were exchanging was a message containing useless 8 characters forming my nickname, ahem.

What if we wanted to exchange informative signals and simulate what might be happening in a real CAN BUS from the dashboard ECU's point of view!

Because it would be waste of time, and bits actually, if you use the 8 data bytes to represent only one piece of information, cars might pack many signals in one message, as shown in the picture above, telling you that for the message of the ID = 0xF1:

  • The first whole byte represents the speed of the car with a maximum value of 255 mph.
  • The next 14 bits represent the engine revs up to 16383 RPM. Just to be able to work with an F1 car.
  • The next bit tells the dashboard to turn On/Off the check engine indicator.
  • The next bit tells whether the oil level is under a threshold or not to turn ON/OFF its indicator.
  • The rest of the bits are not used in our made-up message.

Check the attached files for the code, and come back here for the explanation.


For the first piece of information, as it fits in one byte and our code let us deal with every byte of the buffer separately, all you need to do here is to extract the car speed directly from the first byte only, easy!

  • i.e. car_speed = buf[0];


For the rest cases where the signal bits take more or less than a byte, you will need bit manipulation to put those bits in the right setup before reading them.

To read the RPM which lies in two different bytes (1 and 2) you need to do as follows:

  • First, read the higher byte (byte 1) into a two-byte integer, it will lay in the lower byte in engine_rpm.
    • unsigned int engine_rpm = buf[1];
  • Then, shift this value 8 bits to the left to put the higher byte you read in its right position.
    • engine_rpm = engine_rpm << 8;
  • Now, mask off all byte 2 bits except the first six bits used for the RPM signal.
    • char temp = buf[2] & 0x3F;
  • Here, we just need to add the higher byte to the lower one and get our final value.
    • engine_rpm = engine_rpm + temp;

To read the value of Check Engine and Oil bits, you will need to mask off all the bits but the one you want, piece of cake!

Tataaaaaa! We correctly extracted all the information embedded in the message!

Step 6: The Evolution of Breadboard to PCB

After testing and making sure that everything is working properly, I decided to start learning PCBs making by converting this very project to a PCB.

Attached here are the Eagle files for the layout. The layout was designed, printed on a copper board but not yet milled or tested, you can revise the design and give your comments below, you can also print it and make your own PCB and then tell us what you ended up with. And you can even suggest enhancements to the layout design!

Step 7: Have a Little Chat With Your Car

Have you ever been to your mechanic complaining from a mysterious light in your dashboard and you see him plugging some device to your car ~magic happens~ that shows you a fault code describing in details what the non-mysterious light means?!

Some say, with our CAN setup, we can talk to our cars through their OBD-II port, just like the mechanic in the previous paragraph, by sending the so called PIDs to the OBD requesting some parameter from the car communication buses and wait for the response message carrying the values you asked for.

An example covering this PID part is given in the examples section of the library, and the list of available PIDs is available on wiki.

I didn't try it, but I will do this very soon. Take care of your connections and read about OBD before you plug anything to your car.



Feel free to discuss anything with us below, we are all here to learn from each other, Peace!

First Time Author Contest

Participated in the
First Time Author Contest

Be the First to Share

    Recommendations

    • Make it Glow Contest

      Make it Glow Contest
    • First Time Author Contest

      First Time Author Contest
    • PCB Challenge

      PCB Challenge

    23 Discussions

    0
    william.siffer
    william.siffer

    Question 9 months ago

    Hi- I made this but I cannot seem to get any data from my OBD2 port. I am connected to 6/14 and 2 of the led's are blinking. I am also connected to CAN ground. The problem is it is not getting any data with the car on, instead it only shows symbols like arrows and boxes and question marks. Any ideas?

    0
    omarCartera
    omarCartera

    Answer 9 months ago

    have you got it working since then?

    0
    william.siffer
    william.siffer

    Reply 4 weeks ago

    I was able to get this working. I picked up the project again after the end of my semester. In line 56 of CAN_Recieve, the code is "Serial.write(buf[i]);" but since vehicle CAN is in HEX the correct implementation is "Serial.print(buf[i], HEX);"

    looks like I can get all of my vehicle data now with that change. Thanks for the great project!

    0
    omarCartera
    omarCartera

    Reply 27 days ago

    Exactly, the most important part is to get the raw data, and then cast it as you like :D

    Happy to hear that it worked, good job man :D
    0
    william.siffer
    william.siffer

    Reply 27 days ago

    Also, the code here didn’t seem to work with extended can frames for some reason. But I think it’s a library limitation

    0
    william.siffer
    william.siffer

    Reply 9 months ago

    Not quite. But I took some time off the project to organize my space. Will keep this thread updated

    0
    DrewYu
    DrewYu

    7 weeks ago

    Hi there, excuse my ignorance but I was just wondering what you have the Arduino plugged into in your first picture? It is some sort of small RC car with a CAN Bus? I'm looking for some sort of HW Simulator for CAN Bus and that would be awesome!

    0
    william.siffer
    william.siffer

    Reply 4 weeks ago

    I believe that is just an artistic representation. CAN is mostly used for systems with many devices (nodes) that all need to talk to each other. RC cars don't have many components so it would be expensive for a company to implement CAN in a toy like that.

    0
    steelhanzero
    steelhanzero

    Question 9 months ago on Step 7

    hello~
    I have got a question.
    in your source code use a 1 ID of buffer..
    If i want to use a 3 id of buffer data..
    what can i change the code?

    like
    if(canID=0x01) {.a=buf[1]....}
    if(canID=0x02) { b=buf[1]...}
    if(canID=0x03) {.c=buf[1]...}

    is it ok?

    i tried but it may had a buffer problems..


    0
    omarCartera
    omarCartera

    Answer 9 months ago

    I can't understand exactly which part of which code do you mean, but as far as I understand we were reading the messages with only one id, you want to read more different messages.

    Yes, using if .. else if is a nice idea and it should work, what is your problem then?

    0
    steelhanzero
    steelhanzero

    Reply 9 months ago

    hello. thanks for the reply. i attached my sour code.
    some time it work. some time doesn't work..
    i don't know what is the problems.

    check the 2 can id using the if....
    and then send the can value to cluster..


    #include <mcp_can.h>
    #include <SPI.h>


    long unsigned int rxId;
    unsigned char len;
    unsigned char rxBuf[8];
    boolean k = false;
    boolean aj = false;
    MCP_CAN CAN0(9); // Set CS to pin 9
    void setup()
    {
    Serial.begin(115200);
    if(CAN0.begin(MCP_STD, CAN_500KBPS, MCP_8MHZ) == CAN_OK) Serial.print("MCP2515 Init Okay!!\r\n");
    else Serial.print("MCP2515 Init Failed!!\r\n");

    CAN0.setMode(MCP_NORMAL);
    }

    void loop()
    {
    if(!digitalRead(2)) // If pin 2 is low, read receive buffer
    {
    CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s)
    if(rxId == 1056 && (rxBuf[1] & 0x04)) { // ID=1056 alert check state1 and ID=1056's value of rxBuf[1]
    k=1;
    }
    if(rxId == 1461 && k==1 && rxBuf[1]==0x01) { // ID=1461 alert check state2 and ID=1461's value of rxBuf[1]
    aj=1;
    }
    if(rxId == 1264 && aj==1) { //send cluster for release alert value of ID=1264's value of rxBuf[0] plus 2
    k=0;
    aj=0;

    unsigned char stmp[8] = {rxBuf[0]+2,rxBuf[1],rxBuf[2],rxBuf[3],rxBuf[4],rxBuf[5],rxBuf[6],rxBuf[7]}; //value of rxid=1264's values.
    CAN0.sendMsgBuf(rxId,0,8,stmp); // rxId = 1264
    Serial.println("sending");
    Serial.println(rxBuf[0]+2);
    delay(1);
    }
    }
    }

    0
    Abd2490
    Abd2490

    Question 1 year ago on Step 3

    hi,
    can someone explain what is sending CAN messages from arduino, we actually need to extract the messages from the CAN bus if I have understood correctly, i am a beginner kindly assist ?

    0
    EricN137
    EricN137

    Answer 1 year ago

    Dear Abd,
    your question is not entirely clear, but the Arduino sends messages to the MCP chips, which translates CAN to Arduino understandable language and vice versa. With the CAN Read code you can extract CAN messages and print them in the Arduino compiler

    kind regards
    Eric

    1
    JSobell
    JSobell

    1 year ago

    I think it either has to be
    engine_rpm = ((buf[2] & 0x3F)<< 8) | buf[1]
    or
    engine_rpm = (buf[1] << 6) | (buf[2] & 0x3F)

    The example given is adding 0-63 to a number that is a multiple of 256, so assuming buf[1] is the low bits, the first applies. If the lowest bits are in buf[2] then the second applies.

    Regardless of that minor issue, it's a nice example! Good job!

    0
    XOIIO
    XOIIO

    Reply 1 year ago

    Were you able to actually get the rpm info from a car with this? I've been trying to get info from my OBD port but it just isn't working for some reason, it initializes the canbus chips but doesn't put out anything else through the serial port.

    0
    omarCartera
    omarCartera

    Reply 1 year ago

    Sorry for the so late response, but it worked with me and many guys also contacted me reporting that it worked as well.

    1
    JSobell
    JSobell

    Reply 1 year ago

    By the way, the fact I read all the way through and even analysed the code shows how much I enjoyed the article :)

    0
    omarCartera
    omarCartera

    Reply 1 year ago

    Thank you for passing by, really appreciate this :D

    But as I can grab from the back of my memory that I tested the procedure in my code the other day and it gave the expected number, 10542 as stated in the sender source file.

    and yes, buf[1] holds the most significant byte.

    I worked yours also quickly on a white paper and I believe it gives the same result. Please notify my if I'm still getting something wrong.

    Thank you again, it seems you just signed up to put these precious comments :D :D

    0
    Rickson Chattergoon
    Rickson Chattergoon

    Question 1 year ago on Step 7

    Hello there. I have a BMW e46 which uses can bus. The engine gave up the ghost so I decided to put in an rb25 from an old skyline. Now I want to connect the ecu from that engine to the can bus network in the car. Could I use arduino with can shield to link the ecu to the can bus network?

    0
    my4m
    my4m

    2 years ago

    Thanks for your instructions! I use this codes and now I can send frame and receive from the other board. I check the serial monitor of Arduino and the ID and data are correct, but when I check the canH and canL signals on oscilloscope shouldn't I see the frame?!! I just get pulse that it isn't correct frame. Would you please help me about it?