How to use MCP2515 CAN Bus with Arduino.

MCP2515 CAN Bus modules with Arduino

One of the challenges that automobile manufacturers faced in the early days was monitoring a large number of Electronic Control Units (ECUs) for controlling aspects of the car like tire pressure, temperature, airbags, cruise control system and others. This was solved with the invention of the CAN protocol which is now used not only in automobiles but also in other industrial applications.

What is CAN protocol?

CAN is short for Controller Area Network which is a two-wire serial bus communication protocol developed by Bosch to exchange information between Electronic Control Units in automobiles. This system makes possible to share a great amount of information between nodes and control units without complex wiring.

The Controller Area Network acts like a nervous system where the ‘nodes’ or ‘electronic control units’ (ECUs) are like parts of the body interconnected via the CAN bus. Information sensed by one part can be shared with another.

Car Electronic Control Units with CAN Bus

An ECU can prepare and broadcast information via the CAN bus. The broadcasted data is accepted by all other ECUs on the CAN network and each ECU can then check the data and decide whether to receive or ignore it.

Two CAN versions are now in use; CAN 2.0A, which is the the low-speed version known as Basic or Standard CAN and  CAN 2.0B, the high-speed version, also known as Full CAN or extended-frame CAN.

Communication over the CAN bus is done via CAN frames and the structure of a standard CAN frame is shown below.

Structure of a standard CAN frame
  • SOF (Start Of Field): The Start of Frame is a ‘dominant 0’ to tell the other nodes that a CAN node intends to talk
  • Identifier (ID): represents the priority of the message with lower values having higher priority
  • RTR (Remote Transmission Request): indicates whether a node sends data or requests dedicated data from another node
  • Control Field: contains the Identifier Extension Bit (IDE) which is a ‘dominant 0’ for 11-bit. It also contains the 4 bit Data Length Code (DLC) that specifies the length of the data bytes to be transmitted (0 to 8 bytes)
  • Data Field: The Data contains the data bytes aka payload, which includes CAN signals that can be extracted and decoded for information
  • CRC (The Cyclic Redundancy Check): is used to ensure data integrity
  • ACK (Acknowledgement): indicates whether the node has acknowledged and received the data correctly
  • EOF (End Of Field): marks the end of the CAN frame

The two CAN frame components that require user interaction are the CAN ID and Data field.

MCP2515 CAN module Overview.

The MCP2515 CAN Bus Controller is a simple Module that supports CAN Protocol version 2.0B and can be used for communication at 1Mbps.

MCP2515 CAN module
  • INT – Interrupt
  • SCK – Clock
  • SI – SPI Master Out Slave In
  • SO– SPI Master In Slave Out
  • CS – SPI Slave Select
  • GND – Ground Pin
  • VCC – 5V Power input

The following image shows the schematic of how MCP2515 and TJA1050 ICs are connected in the MCP2515 CAN module.

Schematic of how MCP2515 and TJA1050 ICs are connected

The MCP2515 IC is the main controller and consists of ;

  • CAN Module responsible for transmitting and receiving messages on the CAN Bus.
  • Control Logic handles the setup and operation of the MCP2515 by interfacing all the blocks.
  • The SPI Block responsible for SPI Communication interface.

The TJA1050 IC acts as an interface between MCP2515 CAN Controller and the physical CAN Bus, therefore is responsible for taking data from the controller and relaying it on to the bus.

How to use MCP2515 CAN bus modules with Arduino.

Since the CAN Controller IC uses SPI Communication Protocol for interfacing with a microcontroller, the connections below will be used for the MCP2515 Module to corresponding SPI Pins of Arduino UNO.

MCP2515 ModuleArduino
VCC5V
GNDGND
CSDigital Pin 10
SODigital Pin 12
SIDigital Pin 11
SCKDigital Pin 13
INTDigital Pin 2

In order to setup a complete communication system, you will need two CAN Bus Modules, one acts as a transmitter and the other as a receiver. Connect CANH and CANL pins of each MCP2515 Module to enable communication between the transmitter and receiver.

Schematic showing how to  connect MCP2515 CAN modules with Arduino

I have included a DHT11 sensor and potentiometer on the transmitter side and a 16×2 I2C LCD on the receiver side to display the readings from the transmitter.

Before proceeding you need to know how to interface the DHT11 sensor, potentiometer and I2C LCD with Arduino. You can check out the tutorials below for reference:

Code for Controlling MCP2515 CAN module with Arduino

Since the communication involves a Transmitter Module and a Receiver Module, the code is also divided into Transmitter Code and Receiver Code.

Transmitter Code

#include <SPI.h>          
#include <mcp2515.h>      
#include <DHT.h>           

#define DHTPIN A1       
#define DHTTYPE DHT11
#define potPin A0

int potValue = 0;
DHT dht(DHTPIN, DHTTYPE);     

struct can_frame canMsg;   
struct can_frame canMsg1; 
MCP2515 mcp2515(10);  

void setup() 
{
  while (!Serial);
  Serial.begin(9600);
  
  SPI.begin();               
  dht.begin();             
  
  mcp2515.reset();
  mcp2515.setBitrate(CAN_500KBPS,MCP_8MHZ); 
  mcp2515.setNormalMode();

  canMsg.can_id = 0xAA; 
  canMsg.can_dlc = 2;   

  canMsg.can_id  = 0x036;           
  canMsg.can_dlc = 8;               
}

void loop() 
{
  int h = dht.readHumidity();      
  int t = dht.readTemperature();   
  potValue = analogRead(potPin);
  potValue = map(potValue,0,1023,0,255);

  canMsg1.data[0] = potValue; 
  canMsg1.data[1] = 0x00;
  mcp2515.sendMessage(&canMsg1); 
  delay(200);  

  canMsg.data[0] = t;               
  canMsg.data[1] = h;               
  canMsg.data[2] = 0x00;            
  canMsg.data[3] = 0x00;
  canMsg.data[4] = 0x00;
  canMsg.data[5] = 0x00;
  canMsg.data[6] = 0x00;
  canMsg.data[7] = 0x00;
  mcp2515.sendMessage(&canMsg);     
  delay(1000);
}

Code description

First we include the SPI.h library for using SPI Communication, mcp2515.h library for using CAN Communication and DHT.h library for using DHT sensor with Arduino.

Pins A0 and A1 are defined as connections for the potentiometer and DHT11 sensor respectively. Also the type of DHT sensor being used is defined as DHT11.

Then we create and initialize the dht object from DHT class. The dht object contains the DHT pin connected to Arduino and DHT type as DHT11.   

canMsg and canMsg1 struct data type are for storing CAN messages received from the potentiometer and DHT11 sensor.

Next the mcp2515 object is initiated from class MCP2515 by passing the CS pin chip select pin 10 , as argument.

In the setup() section, begin the SPI communication by using SPI.begin();  and and also begin receiving temperature and humidity values from the DHT11 sensor using dht.begin();              

We call the functions of object mcp2515 to Reset the MCP2515 using .reset(); and set a speed of 500KBPS and a clock of 8MHZ using .setBitrate(CAN_500KBPS,MCP_8MHZ);

Also set the MCP2525 at normal mode using setNormalMode();

can_id is the user generated HEX ID for example I have used 0xAA for the potentiometer message and 0x036 for the DHT11 message.

can_dlc is the length of data with a maximum of 8 bytes allowed. For demonstration purposes, I have used can_dlc of 2 bytes for the potentiometer and 8 bytes for the DHT11 sensor.

In the loop() section, first we get the Humidity and Temperature values and store them as integer variables h and t using dht.readHumidity(); and dht.readTemperature();. The potentiometer values are also read and converted to a range of 0~255 using map() function.

Data members of struct are assigned using a dot operator for example, for the DHT11 sensor where 8 bytes were used, we update the h and t value in the data[0]  and data[1]  and the rest of the bytes will 0.

Finally, the data is sent over CAN bus using sendMessage() function of mcp2515, that is, mcp2515.sendMessage(&canMsg);    

Receiver Code

On the receiver side the Arduino UNO receives the Temperature, Humidity and Potentiometer readings from CAN bus and displays the data received on the 16×2 LCD. The code below is used to achieve this:

#include <SPI.h>
#include <mcp2515.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2); 

struct can_frame canMsg;
struct can_frame canMsg1;

MCP2515 mcp2515(10);


void setup() {
  lcd.begin();
  lcd.backlight();
  lcd.setCursor(0,0);               
  lcd.print("MYTECTUTOR");
  lcd.setCursor(0,1);
  lcd.print("ARDUINO CAN");
  delay(3000);
  lcd.clear();

  SPI.begin();
  
  mcp2515.reset();
  mcp2515.setBitrate(CAN_125KBPS,MCP_8MHZ);
  mcp2515.setNormalMode();
}

void loop() {
  if (mcp2515.readMessage(&canMsg1) == MCP2515::ERROR_OK) {
     if(canMsg1.can_id==0xAA){
       int x = canMsg1.data[0];
       lcd.setCursor(0,0);
       lcd.print("potVal:");
       lcd.print(x);
       delay(200);
      }   
    }

  if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK){ 
      if(canMsg.can_id==0xAA){
         int y = canMsg.data[0];         
         int z = canMsg.data[1];       
         lcd.setCursor(0,1);         
         lcd.print("Temp:");
         lcd.print(y);
         lcd.print((char)223);
         lcd.print("C");
         lcd.setCursor(10,1);
         lcd.print("H:");
         lcd.print(z);
         lcd.print("%");
         delay(1000);
         lcd.clear();
      }
    }
}

Code description

First include libraries, SPI.h for SPI Communication, mcp2515.h for using CAN Communication, Wire.h and LiquidCrsytal_I2C.h for using the 16×2 I2C LCD with Arduino.

In the beginning parts and setup() section, the receiver code is similar to the transmitter code apart from the additional initialization of the I2C LCD.

In the loop() section, we use the readMessage() function of mcp2515 to verify the incoming message and then compare the ids, then extract the message to display on the LCD.