Difference between revisions of "Creating CAN-BUS Shield V2.0 Tutorial"

From Embedded Lab Vienna for IoT & Security
Jump to navigation Jump to search
 
(32 intermediate revisions by the same user not shown)
Line 1: Line 1:
== Summary ==  
== Summary ==  


This documentation introduces the CAN-BUS Shield V2.0 and all its key facts. It also contains a example implementation, which is able to read CAN messages from a real car.  
This documentation introduces the CAN-BUS Shield V2.0 and all its key facts. It also contains an example implementation, which is able to read and write CAN messages. In another step a real car communication is captured and presented.


== CAN-BUS Shield V2.0 ==
== Hardware details ==


The CAN-BUS Shield V2.0 is a add-on shield for Arudion
The CAN-BUS Shield V2.0 is an add-on shield which can be used with an Arduino Board. It comes with the MCP2515 CAN Bus Controller and the MCP2551 CAN transceiver. When used with an appropriate OBD 2.0 cable and library it can function as a diagnostic tool for a car.


== Implementation ==
[[File:InkedCAN-BUS_Shield_V2.0_desc_LI.jpg|500 px]]


=== Requirements ===
# Terminal: CAN-H, CAN-L
# Led Indicator: PWR=power;TX=blink when the data is sending;RX=blink when there's data receiving;INT=data interrupt
# V_OBD: Indicates if the power comes from OBD
# DB9 Interface: to connect to OBD 2.0 via DB9-OBDII-cable
# Arduino Pinouts
# I2C Connector
# Serial Connector
# ICSP Pins


* Operating system: Ubuntu 18.04 bionic amd64
CAN is the abbrevation for Controller Area Network and is a common and reliable bus system. It is used in almost every car built after 1994. CAN was founded by BOSCH primarily to shrink cable harness within cars. This bus is robust because of its differential signal transmission which basically means that the difference between CAN-H and CAN-L signal is used to determine which bit was transfered. CAN uses a broadcast to transfer messages. Each message contains an identifier with which the priority is negotiated. In a real world example the CAN Bus is terminated on each end with a 120k Ohm resistor. Because this is a lab setup and the distance of the CAN Bus is minimal it is not neccessary to terminate the bus with 120k Ohm resistors.
* Packages: git emacs


In order to complete these steps, you should have read this(https://wiki.elvis.science/index.php?title=Arduino_Board_Uno_Rev3-Atmega_328:_First_Steps) before.
In order to complete the following steps, you should be familiar with the Arduino UNO and the Arduino IDE.  
 
== Hands on lab ==


=== Description ===
=== Description ===


In order to create a functioning CAN message reader we need to set up a lab, which consits of two CAN-BUS Shield V2.0 and two Arduino UNOs. The Arduinos will be used to simulate two ECUs, where one of them sends the data and the other is receives it. After this works as planned we will take a step further and integrate the CANtext Bundle. This will enable is to take the reading Arduino-ECU and plug it into the cars OBD 2.0 port in order to see what is going on the car CAN-BUS.
In order to create a functioning CAN message reader we need to set up a lab, which consits of two CAN-BUS Shield V2.0 and two Arduino UNOs. The Arduinos will be used to simulate two ECUs, where one of them sends the data and the other is receives it. After this works as planned we will take a step further and integrate the CANtact Bundle cable. This will enable is to take the CAN-Node (Arduino and CAN-Shield) and plug it into the cars OBD 2.0 port in order to see what is going on on the car CAN-BUS.
[[File:CAN-BUS_Shield_V2.0.jpg|thumb|CAN-BUS Shield V2.0]]
[[File:CAN-BUS_Shield_V2.0.jpg|thumb|CAN-BUS Shield V2.0]]
[[File:Arduino_Uno.jpg|thumb|Arduino UNO]]
[[File:Arduino_Uno.jpg|thumb|Arduino UNO]]
[[File:CANtext_Bundle.jpg|thumb|CANtext Bundle]]
[[File:CANtext_Bundle.jpg|thumb|CANtact Bundle]]


----
----
Line 27: Line 35:
==== Step 1 ====
==== Step 1 ====


Enter these commands in the shell
Pigiback the CAN shields onto the Arduinos and connect both terminals. Bare in mind connecting CAN-H on the first board with CAN-H on the second board and CAN-L on the first board with CAN-L on the second board. This would look something like this:


echo foo
[[File:Arduino_ECUs_connected.jpg|500 px]]
echo bar


==== Step 2 ====
==== Step 2 ====
Download and install the Arduino IDE. Afterwards we need to install the CAN-Shield library which can be found here https://github.com/Seeed-Studio/Seeed_Arduino_CAN. Download this repository as zip file and import it to the Arduino IDE:
[[File:Add_lib.JPG|750px]]


Make sure to read
==== Step 3 ====
Next we are going to need a sending node, which will send CAN messages at periodic intervalls and we will need an receiving node, which listens on the CAN Bus and creates an interrupt everytime a package is received.


* War and Peace
The following code is based on the send example from the library and needs to be flashed on the first node.
* Lord of the Rings
<syntaxhighlight lang="c">
* The Baroque Cycle
#include <SPI.h>
#ifdef ARDUINO_SAMD_VARIANT_COMPLIANCE
    #define SERIAL SerialUSB
#else
    #define SERIAL Serial
#endif
 
#define CAN_2515
#ifdef CAN_2518FD
#include "mcp2518fd_can.h"
const int SPI_CS_PIN = 9;
const int CAN_INT_PIN = 2;
mcp2518fd CAN(SPI_CS_PIN); // Set CS pin
#endif
 
#ifdef CAN_2515
#include "mcp2515_can.h"
const int SPI_CS_PIN = 9;
const int CAN_INT_PIN = 2;
mcp2515_can CAN(SPI_CS_PIN); // Set CS pin
#endif                              // Set CS pin
 
void setup() {
    SERIAL.begin(115200);
    while(!Serial){};
 
#ifdef CAN_2518FD
    while (0 != CAN.begin((byte)CAN_500K_1M)) {            // init can bus : baudrate = 500k
#else
    while (CAN_OK != CAN.begin(CAN_500KBPS)) {            // init can bus : baudrate = 500k
#endif       
        SERIAL.println("CAN BUS Shield init fail");
        SERIAL.println(" Init CAN BUS Shield again");
        delay(100);
    }
    SERIAL.println("CAN BUS Shield init ok!");
}
 
unsigned char stmp[8] = {0, 0, 0, 0, 0, 0, 0, 0};
void loop() {
    // send data:  id = 0x00, standrad frame, data len = 8, stmp: data buf
    stmp[7] = stmp[7] + 1;
    if (stmp[7] == 100) {
        stmp[7] = 0;
        stmp[6] = stmp[6] + 1;
 
        if (stmp[6] == 100) {
            stmp[6] = 0;
            stmp[5] = stmp[6] + 1;
        }
    }
 
    CAN.sendMsgBuf(0x00, 0, 8, stmp);
    delay(100);                      // send data per 100ms
    SERIAL.println("CAN BUS sendMsgBuf ok!");
}
</syntaxhighlight>
 
Next we need the code for the receiving node.
<syntaxhighlight lang="c">
#include <SPI.h>
 
#ifdef ARDUINO_SAMD_VARIANT_COMPLIANCE
    #define SERIAL SerialUSB
#else
    #define SERIAL Serial
#endif
 
#define CAN_2515
 
#ifdef CAN_2518FD
#include "mcp2518fd_can.h"
const int SPI_CS_PIN = 9;
const int CAN_INT_PIN = 2;
mcp2518fd CAN(SPI_CS_PIN); // Set CS pin
#endif
 
#ifdef CAN_2515
#include "mcp2515_can.h"
const int SPI_CS_PIN = 9;
const int CAN_INT_PIN = 2;
mcp2515_can CAN(SPI_CS_PIN); // Set CS pin
#endif                              // Set CS pin                         
 
void setup() {
    SERIAL.begin(115200);
    while(!Serial){};
 
#ifdef CAN_2518FD
    while (0 != CAN.begin((byte)CAN_500K_1M)) {            // init can bus : baudrate = 500k
#else
    while (CAN_OK != CAN.begin(CAN_500KBPS)) {            // init can bus : baudrate = 500k
#endif
        SERIAL.println("CAN BUS Shield init fail");
        SERIAL.println(" Init CAN BUS Shield again");
        delay(100);
    }
    SERIAL.println("CAN BUS Shield init ok!");
}
 
void loop() {
    unsigned char len = 0;
    unsigned char buf[8];
 
    if (CAN_MSGAVAIL == CAN.checkReceive()) {        // check if data coming
        CAN.readMsgBuf(&len, buf);    // read data,  len: data length, buf: data buf
 
        unsigned long canId = CAN.getCanId();
 
        SERIAL.println("-----------------------------");
        SERIAL.print("Get data from ID: 0x");
        SERIAL.println(canId, HEX);
 
        for (int i = 0; i < len; i++) { // print the data
            SERIAL.print(buf[i], HEX);
            SERIAL.print("\t");
        }
        SERIAL.println();
    }
}
</syntaxhighlight>
 
 
==== Step 4 ====
When both Arduinos are flashed correctly the will start blinking. The sending nodes TX and RX LEDs will blink as CAN transceivers listen and write to the bus simultaneously. On the receiving node the RX and the INT LEDs will start to blink. Everytime a CAN message is received an interrupt is triggered to run the specified function which in this case is printing the raw data to SERIAL:
 
[[File:Nodes_talking.gif]]
 
[[File:Nodes_talking_console.JPG|1000 px]]
 
=== Taking it further ===
 
Next we aim to build a CAN message reader for a real car. Therefore we somehow need to save the captured packets somewhere. The CAN-BUS Shield V2.0 comes with a built in memory card slot. We will utilize it and save captured data onto it. In order to do that we need to include the SD.h header file.
 
<syntaxhighlight lang="c">
#include <SD.h>
</syntaxhighlight>
 
In the setup function we need to activate SD module.
<syntaxhighlight lang="c">
if (!SD.begin(4))
{
    SERIAL.println("SD Card init failed. Check PIN");
}
</syntaxhighlight>
 
After this has happend we can start opening a file and writing to it.
<syntaxhighlight lang="c">
 
File dataFile = SD.open("can.log", FILE_WRITE);
if (dataFile)
{
    dataFile.println("-----------------------------");
    dataFile.print("Get data from ID: 0x");
    dataFile.println(canId, HEX);
    for (int i = 0; i < len; i++)
    { // print the data
        dataFile.print(buf[i], HEX);
        dataFile.print("\t");
    }
    dataFile.println();
    dataFile.close();
}
else
{
    Serial.println("error opening datalog.txt");
}
</syntaxhighlight>
 
Unfortunately I ran into troubles with the writing speeds. CAN is sending data way much faster than it can be written to a file so the Arduino starts to stuck and cannot open the file anymore. A file on a SD card can take from 15ms up to 50ms for opening. The lab was setup with a sending interval of 100ms. In a real world card the freuquency would be much higher so logging on the SD card won't work. When slowing down the speed to 500ms a CAN.LOG file is genereated and populated as programmed.
 
[[File:Can_log_file.JPG|750px]]


=== Used Hardware ===
=== Used Hardware ===


[[Device to be used with this documentation]]
2 x [[Arduino Board Uno Rev3 DIP Version, ATmega328, USB]]
[[Maybe another device to be used with this documentation]]
 
2 x [[CAN-BUS Shield V2.0]]


== Courses ===
2 x Jumper wires


* [[A course where this documentation was used]] (2017, 2018)
2 x USB 2.0 B to USB 2.0 A cables
* [[Another one]] (2018)


== References ===
== References ==


* https://wikipedia.org
* https://ww1.microchip.com/downloads/en/DeviceDoc/MCP2515-Stand-Alone-CAN-Controller-with-SPI-20001801J.pdf
* https://google.com
* https://www.bosch.com/de/stories/das-controller-area-network/
* https://wiki.seeedstudio.com/CAN-BUS_Shield_V2.0/


[[Category:Documentation]]
[[Category:Documentation]]

Latest revision as of 18:32, 24 February 2021

Summary

This documentation introduces the CAN-BUS Shield V2.0 and all its key facts. It also contains an example implementation, which is able to read and write CAN messages. In another step a real car communication is captured and presented.

Hardware details

The CAN-BUS Shield V2.0 is an add-on shield which can be used with an Arduino Board. It comes with the MCP2515 CAN Bus Controller and the MCP2551 CAN transceiver. When used with an appropriate OBD 2.0 cable and library it can function as a diagnostic tool for a car.

InkedCAN-BUS Shield V2.0 desc LI.jpg

  1. Terminal: CAN-H, CAN-L
  2. Led Indicator: PWR=power;TX=blink when the data is sending;RX=blink when there's data receiving;INT=data interrupt
  3. V_OBD: Indicates if the power comes from OBD
  4. DB9 Interface: to connect to OBD 2.0 via DB9-OBDII-cable
  5. Arduino Pinouts
  6. I2C Connector
  7. Serial Connector
  8. ICSP Pins

CAN is the abbrevation for Controller Area Network and is a common and reliable bus system. It is used in almost every car built after 1994. CAN was founded by BOSCH primarily to shrink cable harness within cars. This bus is robust because of its differential signal transmission which basically means that the difference between CAN-H and CAN-L signal is used to determine which bit was transfered. CAN uses a broadcast to transfer messages. Each message contains an identifier with which the priority is negotiated. In a real world example the CAN Bus is terminated on each end with a 120k Ohm resistor. Because this is a lab setup and the distance of the CAN Bus is minimal it is not neccessary to terminate the bus with 120k Ohm resistors.

In order to complete the following steps, you should be familiar with the Arduino UNO and the Arduino IDE.

Hands on lab

Description

In order to create a functioning CAN message reader we need to set up a lab, which consits of two CAN-BUS Shield V2.0 and two Arduino UNOs. The Arduinos will be used to simulate two ECUs, where one of them sends the data and the other is receives it. After this works as planned we will take a step further and integrate the CANtact Bundle cable. This will enable is to take the CAN-Node (Arduino and CAN-Shield) and plug it into the cars OBD 2.0 port in order to see what is going on on the car CAN-BUS.

CAN-BUS Shield V2.0
Arduino UNO
CANtact Bundle

Step 1

Pigiback the CAN shields onto the Arduinos and connect both terminals. Bare in mind connecting CAN-H on the first board with CAN-H on the second board and CAN-L on the first board with CAN-L on the second board. This would look something like this:

Arduino ECUs connected.jpg

Step 2

Download and install the Arduino IDE. Afterwards we need to install the CAN-Shield library which can be found here https://github.com/Seeed-Studio/Seeed_Arduino_CAN. Download this repository as zip file and import it to the Arduino IDE: Add lib.JPG

Step 3

Next we are going to need a sending node, which will send CAN messages at periodic intervalls and we will need an receiving node, which listens on the CAN Bus and creates an interrupt everytime a package is received.

The following code is based on the send example from the library and needs to be flashed on the first node.

#include <SPI.h>
#ifdef ARDUINO_SAMD_VARIANT_COMPLIANCE
    #define SERIAL SerialUSB
#else
    #define SERIAL Serial
#endif

#define CAN_2515
#ifdef CAN_2518FD
#include "mcp2518fd_can.h"
const int SPI_CS_PIN = 9;
const int CAN_INT_PIN = 2;
mcp2518fd CAN(SPI_CS_PIN); // Set CS pin
#endif

#ifdef CAN_2515
#include "mcp2515_can.h"
const int SPI_CS_PIN = 9;
const int CAN_INT_PIN = 2;
mcp2515_can CAN(SPI_CS_PIN); // Set CS pin
#endif                              // Set CS pin

void setup() {
    SERIAL.begin(115200);
    while(!Serial){};

#ifdef CAN_2518FD
    while (0 != CAN.begin((byte)CAN_500K_1M)) {            // init can bus : baudrate = 500k
#else
    while (CAN_OK != CAN.begin(CAN_500KBPS)) {             // init can bus : baudrate = 500k
#endif        
        SERIAL.println("CAN BUS Shield init fail");
        SERIAL.println(" Init CAN BUS Shield again");
        delay(100);
    }
    SERIAL.println("CAN BUS Shield init ok!");
}

unsigned char stmp[8] = {0, 0, 0, 0, 0, 0, 0, 0};
void loop() {
    // send data:  id = 0x00, standrad frame, data len = 8, stmp: data buf
    stmp[7] = stmp[7] + 1;
    if (stmp[7] == 100) {
        stmp[7] = 0;
        stmp[6] = stmp[6] + 1;

        if (stmp[6] == 100) {
            stmp[6] = 0;
            stmp[5] = stmp[6] + 1;
        }
    }

    CAN.sendMsgBuf(0x00, 0, 8, stmp);
    delay(100);                       // send data per 100ms
    SERIAL.println("CAN BUS sendMsgBuf ok!");
}

Next we need the code for the receiving node.

#include <SPI.h>

#ifdef ARDUINO_SAMD_VARIANT_COMPLIANCE
    #define SERIAL SerialUSB
#else
    #define SERIAL Serial
#endif

#define CAN_2515

#ifdef CAN_2518FD
#include "mcp2518fd_can.h"
const int SPI_CS_PIN = 9;
const int CAN_INT_PIN = 2;
mcp2518fd CAN(SPI_CS_PIN); // Set CS pin
#endif

#ifdef CAN_2515
#include "mcp2515_can.h"
const int SPI_CS_PIN = 9;
const int CAN_INT_PIN = 2;
mcp2515_can CAN(SPI_CS_PIN); // Set CS pin
#endif                              // Set CS pin                           

void setup() {
    SERIAL.begin(115200);
    while(!Serial){};

#ifdef CAN_2518FD
    while (0 != CAN.begin((byte)CAN_500K_1M)) {            // init can bus : baudrate = 500k
#else
    while (CAN_OK != CAN.begin(CAN_500KBPS)) {             // init can bus : baudrate = 500k
#endif
        SERIAL.println("CAN BUS Shield init fail");
        SERIAL.println(" Init CAN BUS Shield again");
        delay(100);
    }
    SERIAL.println("CAN BUS Shield init ok!");
}

void loop() {
    unsigned char len = 0;
    unsigned char buf[8];

    if (CAN_MSGAVAIL == CAN.checkReceive()) {         // check if data coming
        CAN.readMsgBuf(&len, buf);    // read data,  len: data length, buf: data buf

        unsigned long canId = CAN.getCanId();

        SERIAL.println("-----------------------------");
        SERIAL.print("Get data from ID: 0x");
        SERIAL.println(canId, HEX);

        for (int i = 0; i < len; i++) { // print the data
            SERIAL.print(buf[i], HEX);
            SERIAL.print("\t");
        }
        SERIAL.println();
    }
}


Step 4

When both Arduinos are flashed correctly the will start blinking. The sending nodes TX and RX LEDs will blink as CAN transceivers listen and write to the bus simultaneously. On the receiving node the RX and the INT LEDs will start to blink. Everytime a CAN message is received an interrupt is triggered to run the specified function which in this case is printing the raw data to SERIAL:

Nodes talking.gif

Nodes talking console.JPG

Taking it further

Next we aim to build a CAN message reader for a real car. Therefore we somehow need to save the captured packets somewhere. The CAN-BUS Shield V2.0 comes with a built in memory card slot. We will utilize it and save captured data onto it. In order to do that we need to include the SD.h header file.

#include <SD.h>

In the setup function we need to activate SD module.

if (!SD.begin(4))
{
    SERIAL.println("SD Card init failed. Check PIN");
}

After this has happend we can start opening a file and writing to it.

File dataFile = SD.open("can.log", FILE_WRITE);
if (dataFile)
{
    dataFile.println("-----------------------------");
    dataFile.print("Get data from ID: 0x");
    dataFile.println(canId, HEX);
    for (int i = 0; i < len; i++)
    { // print the data
        dataFile.print(buf[i], HEX);
        dataFile.print("\t");
    }
    dataFile.println();
    dataFile.close();
}
else
{
    Serial.println("error opening datalog.txt");
}

Unfortunately I ran into troubles with the writing speeds. CAN is sending data way much faster than it can be written to a file so the Arduino starts to stuck and cannot open the file anymore. A file on a SD card can take from 15ms up to 50ms for opening. The lab was setup with a sending interval of 100ms. In a real world card the freuquency would be much higher so logging on the SD card won't work. When slowing down the speed to 500ms a CAN.LOG file is genereated and populated as programmed.

Can log file.JPG

Used Hardware

2 x Arduino Board Uno Rev3 DIP Version, ATmega328, USB

2 x CAN-BUS Shield V2.0

2 x Jumper wires

2 x USB 2.0 B to USB 2.0 A cables

References