Search

Arduino and MCP2515: Build Your Own CAN Bus Network

In the past, cars were equipped with over 2,000 meters of cable, directly linking dashboard switches to components like headlights and taillights. As automotive technology advanced, this straightforward system became impractical. To address this, Bosch introduced the CAN bus system in 1986, which streamlined manufacturing and reduced costs. Today, CAN is the industry standard, found in a wide range of vehicles, including cars, trucks, buses, tractors, airplanes, and ships.

By learning to read and interpret CAN messages with your Arduino, you can access various car data such as coolant temperature, throttle position, vehicle speed, and engine RPM, and integrate this information into your dashboard projects.

The MCP2515 CAN Bus Interface Module is an ideal solution for adding CAN connectivity to an Arduino via the SPI interface. This tutorial will guide you through connecting the MCP2515 module to an Arduino. But first, let’s cover the basics of the CAN protocol.

Basics of CAN Bus System

The Controller Area Network (CAN bus) is a communication standard that enables devices within a vehicle to communicate with each other without needing a central computer.

To understand the CAN bus, think of it as the nervous system of a car. Just as the nervous system allows different body parts to communicate, the CAN bus allows communication between various CAN bus nodes, also known as Electronic Control Units (ECUs).

Modern vehicles contain over 70 ECUs, each handling a specific function. While these ECUs are efficient at their individual tasks, they need to exchange information. For instance, the engine control module sends the current engine speed to the instrument cluster to be displayed on the tachometer. Similarly, the driver’s door controller can signal the passenger’s door controller to operate the window.

ECUs are linked in a multi-master configuration, meaning each ECU can control the bus and broadcast information, such as sensor data. All other ECUs on the CAN bus receive this broadcast and can decide whether to use or ignore the information.

CAN Bus Topology

The CAN bus wiring harness facilitates communication, consisting of two tightly twisted wires: CAN low and CAN high. This twisting helps prevent electromagnetic interference from affecting the signals.

The ends of the cable are terminated with 120-ohm resistors to prevent signal reflection, which could disrupt communication and cause the bus to fail.

CAN Signaling

Data transmission on the CAN bus is achieved by altering the voltage levels on the wires. These voltage changes are interpreted as logic levels for network communication.

  • Recessive State (logic 1): Both lines are at 2.5 volts, indicating the bus is free.
  • Dominant State (logic 0): CAN high is at 3.5 volts and CAN low at 1.5 volts, indicating active transmission and that other nodes should wait.

CAN Bus Node Components

Each CAN node includes a CAN transceiver, a CAN controller, and a microcontroller:

CAN Transceiver:

  • Receiving: Converts bus voltage levels to readable levels for the CAN controller.
  • Transmitting: Converts data from the CAN controller to bus voltage levels.

CAN Controller:

  • Transmitting: Sends messages from the microcontroller serially onto the bus when it is free.
  • Receiving: Collects serial bits from the bus until a full message is available, then notifies the microcontroller.

Microcontroller:

Interprets received messages and decides what to transmit. It connects to sensors, actuators, and control devices.

Standard CAN Frame

Communication over the CAN bus occurs via CAN frames. Here’s an overview of an 11-bit identifier CAN frame:

  • SOF (Start of Frame): A ‘dominant 0’ signaling the start of communication.
  • ID (Identifier): Indicates the message content and sender, also determining priority (lower ID means higher priority).
  • RTR (Remote Transmission Request): Specifies whether a node sends or requests data.
  • Control: Contains the Identifier Extension Bit (IDE) and the Data Length Code (DLC), specifying the data byte count.
  • Data: Up to 8 bytes of actual information.
  • CRC (Cyclic Redundancy Check): For error detection.
  • ACK (Acknowledgment): Indicates receipt and correctness of the data.
  • EOF (End of Frame): Marks the frame’s end.
Unlike typical distributed networks where devices have unique IDs, CAN bus nodes do not have IDs. Instead, each message has a unique CAN ID, indicating its content. All nodes receive all messages and filter out the irrelevant ones.

MCP2515 Module Hardware Overview

The MCP2515 CAN Bus Interface Module is a comprehensive CAN solution featuring the MCP2515 CAN controller from Microchip and the TJA1050 High-Speed CAN transceiver from Philips. It is the optimal choice for adding CAN connectivity to an Arduino using the SPI interface.

Thanks to its reliability and robustness, the MCP2515 Module is suitable for various projects requiring dependable data transfer in noisy environments or over significant distances.

The MCP2515 CAN Controller

The MCP2515 is a standalone CAN controller compliant with the CAN specification, Version 2.0B. It can transmit and receive both standard and extended data and remote frames. The controller includes masks and filters to exclude unwanted messages, thereby reducing the workload on the host MCU.

The MCP2515 also features an interrupt (INT) output pin that can trigger an MCU interrupt when a valid message is received and loaded into one of the receive buffers.

The TJA1050 CAN Transceiver

The TJA1050 transceiver interfaces between the MCP2515 CAN controller and the physical two-wire CAN bus. It meets automotive standards for high-speed (up to 1Mb/s) communication, low quiescent current, electromagnetic compatibility, and electrostatic discharge. The TJA1050 allows up to 110 nodes to be connected to the bus.

CAN Bus Connector

The module includes a 2-pole screw terminal labeled H and L for connecting the CAN bus twisted pair cable. It supports data rates up to 1Mb/s, though the maximum transfer speed depends on the bus line length: 40 meters at 1 Mb/s and 500 meters at 125 kb/s.

Node Termination

To prevent signal reflections, the CAN bus must be terminated with resistors at both ends. The MCP2515 Module includes a 120-ohm termination resistor and a node termination jumper. To enable the resistor, ensure the jumper is in place. If the module is at the first or last node of the CAN network, the jumper should be placed. For a middle node, the jumper should be removed.

Technical Specifications

Here are the specifications:

Operating Voltage4.75 to 5.25V (based on TJA1050 requirements)
CAN specificationVersion 2.0B at 1 Mb/s
Crystal Frequency8Mhz
Transmit BuffersThree transmit buffers with prioritization and abort features
Receive BuffersTwo receive buffers with prioritized message storage
Message FiltersSix 29-bit filters
MasksTwo 29-bit masks
Interrupt OutputsOne interrupt with selectable enables
InterfaceHigh-Speed SPI (10 MHz)

For more information about the MCP2515 CAN controller, please refer to the datasheet below.

For more information about the TJA1050 CAN transceiver, please refer to the datasheet below.

MCP2515 Module Pinout

Let’s examine the pinout of the MCP2515 module.

Input Connector

The breakout pins on one side of the module are used to interface with the microcontroller:

  • INT: Generates an interrupt when a valid message is received and loaded into a receive buffer.
  • SCK: The SPI clock pin.
  • SI (MOSI): Serial data In / Master Out Slave In pin, for data sent from the Arduino to the module.
  • SO (MISO): Serial data Out / Master In Slave Out pin, for data sent from the module to the Arduino.
  • CS: Chip select pin, which must be held low to initiate an SPI transaction.
  • GND: Common ground for power and logic.
  • VCC: Power pin, which should be connected to a 5V power supply.

CAN Bus Connector

On the other side of the module, there is a 2-pin screw terminal and a 2-pin male header for connecting the CAN bus twisted pair cable:

  • L: CAN low signal for the CAN Bus.
  • H: CAN high signal for the CAN Bus.

Parts Required

Component NameBuy Now
Arduino Uno REV3Amazon
MCP2515 CAN Bus ModuleAmazon
Please Note: These are affiliate links. I may make a commission if you buy the components through these links. I would appreciate your support in this way!

Hardware Hookup

Now that we have a detailed understanding of the module, let’s create our own CAN network.

Example 1: Simple Two-Node CAN Network

In this setup, we will create a basic two-node CAN bus network where one node sends a message, and the other receives it.

Power Connections:

Connect the VCC pin on the module to 5V on the Arduino and the GND pin to ground.

SPI Connections:

Wire the SPI pins appropriately. Each Arduino board has specific SPI pin locations:

    • For Arduino UNO/Nano V3.0, connect digital pin 13 to SCK, pin 12 to MISO, pin 11 to MOSI, and pin 10 to CS.
    • If using a different Arduino board, refer to the official documentation for SPI pin locations.

    Interrupt Pin:

    Connect the module’s INT pin to digital pin 2 on the Arduino.

      Construct two identical circuits, one for transmitting and one for receiving. Both circuits will have the same wiring setup.

      CAN Bus Connections:

        • Connect CAN L on one module to CAN L on the other.
        • Connect CAN H on one module to CAN H on the other.
        • While twisted pair cables are recommended for better noise immunity, simple breadboard wires can be used for short distances.

        Bus Termination:

        Since this is a simple two-node CAN network, place the jumper on both modules.

          As bus length increases or in noisy environments, using twisted pair cables and adding shielding becomes crucial.

          Construct the network as shown in your schematic or diagram.

          Example 2: Multi-Node CAN Network

          For a larger CAN network, where multiple nodes send messages and one node relays them to a PC via a serial port:

          Additional Nodes: Other nodes can be added between the two end nodes. They can be spliced in-line or attached to the main bus with a short stub cable, keeping the stub length under 12 inches.

          Bus Termination: Place the jumper on the first and last nodes of the CAN network and remove it from the nodes in between.

            Construct the network as shown in your schematic or diagram.

            Library Installation

            To work with the MCP2515 module, you need to install a suitable library in the Arduino IDE:

            Open the Arduino IDE and navigate to Sketch > Include Library > Manage Libraries…

            Wait for the Library Manager to download the library index and update the list of installed libraries.

            Filter the search by entering ‘mcp2515’.

            Look for the CAN library by Sandeep Mistry.

            Select it and click Install.

              Arduino Example Code

              In this simple test, we’ll send a “Hello World” message over the CAN bus and attempt to decode it. This will help you understand how to use the modules and can serve as a basis for more complex projects.

              Code for the Transmitter Node

              Upload the following sketch to the transmitter node. If you have multiple transmitter nodes on a CAN bus, upload this sketch to each node, but ensure each has a unique message ID.

              #include <CAN.h>
              
              void setup() {
                Serial.begin(9600);
                while (!Serial);
              
                Serial.println("CAN Sender");
              
                // start the CAN bus at 500 kbps
                if (!CAN.begin(500E3)) {
                  Serial.println("Starting CAN failed!");
                  while (1);
                }
              }
              
              void loop() {
                // send packet: id is 11 bits, packet can contain up to 8 bytes of data
                Serial.print("Sending packet ... ");
              
                CAN.beginPacket(0x12);
                CAN.write('h');
                CAN.write('e');
                CAN.write('l');
                CAN.write('l');
                CAN.write('o');
                CAN.endPacket();
              
                Serial.println("done");
              
                delay(1000);
              
                // send extended packet: id is 29 bits, packet can contain up to 8 bytes of data
                Serial.print("Sending extended packet ... ");
              
                CAN.beginExtendedPacket(0xabcdef);
                CAN.write('w');
                CAN.write('o');
                CAN.write('r');
                CAN.write('l');
                CAN.write('d');
                CAN.endPacket();
              
                Serial.println("done");
              
                delay(1000);
              }

              Code for the Receiver Node

              Upload the following sketch to the receiver node.

              #include <CAN.h>
              
              void setup() {
                Serial.begin(9600);
                while (!Serial);
              
                Serial.println("CAN Receiver Callback");
              
                // start the CAN bus at 500 kbps
                if (!CAN.begin(500E3)) {
                  Serial.println("Starting CAN failed!");
                  while (1);
                }
              
                // register the receive callback
                CAN.onReceive(onReceive);
              }
              
              void loop() {
                // do nothing
              }
              
              void onReceive(int packetSize) {
                // received a packet
                Serial.print("Received ");
              
                if (CAN.packetExtended()) {
                  Serial.print("extended ");
                }
              
                if (CAN.packetRtr()) {
                  // Remote transmission request, packet contains no data
                  Serial.print("RTR ");
                }
              
                Serial.print("packet with id 0x");
                Serial.print(CAN.packetId(), HEX);
              
                if (CAN.packetRtr()) {
                  Serial.print(" and requested length ");
                  Serial.println(CAN.packetDlc());
                } else {
                  Serial.print(" and length ");
                  Serial.println(packetSize);
              
                  // only print packet data for non-RTR packets
                  while (CAN.available()) {
                    Serial.print((char)CAN.read());
                  }
                  Serial.println();
                }
              
                Serial.println();
              }
              Notice the loop function is empty for the receiver node. This sketch uses an interrupt to notify the Arduino whenever a valid message is received and loaded into one of the receive buffers.

              Demonstration

              After uploading the sketch, open the serial monitor with a baud rate of 9600. The transmitter node will send a standard CAN packet and an extended CAN packet every second.

              The receiver node will capture these packets and relay the data to the PC via the serial port.

              Leave a Comment