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.

Teacher Notes

Teachers! Did you use this instructable in your classroom?
Add a Teacher Note to share how you incorporated it into your lesson.

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


    • Made with Math Contest

      Made with Math Contest
    • Multi-Discipline Contest

      Multi-Discipline Contest
    • Robotics Contest

      Robotics Contest

    10 Discussions


    7 months ago

    I think it either has to be
    engine_rpm = ((buf[2] & 0x3F)<< 8) | buf[1]
    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!

    4 replies

    Reply 5 months 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.


    Reply 2 months ago

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


    Reply 7 months ago

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


    Reply 7 months 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

    Rickson Chattergoon

    Question 3 months 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?


    1 year 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?


    2 years ago

    Great instructable! sorry if this is a stupid question but when masking the bits for the rpm how did you get the number 0x3f. Thanks.

    1 reply

    Reply 2 years ago

    Good question!

    Consider the byte 2 that contains bits 0 to 5 representing some part of the RPM, and bits 6 and 7 are representing something else.

    If we want to extract only the rpm from this byte, we will need to clear bits 6 and 7 (set them to 0) to remove their weight from the equivalent decimal value for this binary. And this is done by bit-wise adding bits 6 and 7 with 0 to force them to be cleared, and adding the others with 1 to keep as they are.


    The byte is: xxyy yyyy (xx are unwanted, yy yyyy are our RPM)

    bit-wise add this with: 0011 1111 (which is 0x3F)

    the result is: 00yy yyyy (whatever the Ys are will keep unchanged)

    Remember that:

    A & 0 = 0

    A & 1 = A