Say it with a CAN Bus and a Raspberry Pi

/, Education/Say it with a CAN Bus and a Raspberry Pi

Say it with a CAN Bus and a Raspberry Pi

Introduction

A minimal Modis Lock system consists of a Host Controller that houses a Raspberry Pi Compute Module 3 and a Reader unit with an ARM microcontroller.  The Host Controller is the ‘brain’ of digital access control and the reader unit receives remote inputs.  Even though the Host Controller has a powerful quad-core BCM2837 Broadcom SoC as its main engine, it also has the same ARM MCU found inside the Reader unit as a helper.  

One of the main purpose of these MCUs is to manage communicating between two components.  Simply put, the Reader forwards a user’s digital input back to the Host Controller for a validation.  The Host Controller then replies the result of the attempt back to the Reader so that the user can be granted or denied an access.

This guide will look at how such communication can be managed using Controller Area Network, commonly called CAN bus.  You probably heard about CAN bus having more to do with automobiles than a security system.  A generic CAN Bluetooth adapter plugged into an automobile and an app on a smartphone can easily glean basic information about most modern vehicle.

In this post, we will look at how to use two Arduinos and two generic MCP2515 CAN Modules to establish a basic communication link.  I will be using an Arduino Nano as a CAN transmitter, and an Arduino Pro Mini 16Mhz 5V model as a CAN receiver.  We’ll need some jumper wires as well.

Background

I went through a few options for to find the simplest way to establish a CAN link between two nodes.  Some MCU’s will natively support CAN bus, and some will not.  Even if an MCU supports it, like my Teensy 3.6, you will require an additional circuit to accommodate a CAN transceiver like MCP2561.  So we will use a complete CAN module that can work with any MCU with an SPI bus.  If you search online ‘mcp2515 can module,’ you will see many options that look like this;

MCP2515 CAN Module

MCP2515 CAN Module

My modules look pretty much identical to that picture.  Each module has a TJA1050 as the CAN transceiver chip.  The transceiver is responsible for actual driving each of the two CAN bus lines’ ones and zeroes.  When the ‘CAN High’ line rises toward 5V and ‘CAN Low’ line drops toward 0V, it’s called a dominant state (represents 0) and when both lines return to 2.5V, it’s called a recessive state (represents 1).

To facilitate the use of this chip, each module also has a MCP2515 as the CAN controller.  The MCP2515 CAN controller manages communication between the CAN transceiver and the host MCU with its own buffers and filtering logic.  The MCP2515 communicates to and from the host via SPI protocol, but the addition of this protocol doesn’t add too much complexity as you will see.  

Setting up CAN Transmitters with the Arduinos

  1. Connect the wires between the Arduino Nano and CAN module for SPI communication.  This will be the CAN Transmitter.
    • Arduino – CAN
    • 5V – VCC
    • GND – GND
    • D10 – CS (aka Chip Select, aka Slave Select)
    • D11 – SI (aka MOSI, Master-Out-Slave-In)
    • D12 – SO (aka MISO, Master-In-Slave-Out)
    • D13 – CLK (Clock)
  2. For my Arduino Pro Mini which will act as the CAN Receiver, the only difference from the Nano setup above is that the power is labeled VCC.  So you will connect VCC on Pro Mini to VCC on CAN module.

Setting up CAN wires and monitoring line

  1. Take two jumper wires and connect them between each of the High and the Low terminals on the CAN modules.  A high output from one CAN module connects to the other module’s high output, and so on.
  2. I am using a USB-Serial adapter to power my Arduino Pro Mini which will also function as a receiver so I am connecting two more wires from the serial adapter to the Pro Mini as below.  My adapter has a jumper for 3.3v or 5v output, so I set mine to 5V as well.  Lastly, my Pro mini is a 5v model.  Some are only rated for 3.3v and should have a marking on the bottom indicating which model.  5v on 3.3v logic won’t cause an ‘electroBoom’ but it can still damage the MCU permanently.
    • Pro Mini – USB-Serial
    • Gnd – Gnd
    • RAW – 5V
    • TXD – RX (Yes, they are ‘crossed’)
    • RXD – TX (‘Crossed’ here again)
  3. Lastly I plug in USB adapters to my Nano and the serial adapter.

Programming each Arduino

1.  If you haven’t yet, install an Arduino IDE available here.  I used the downloadable IDE.

2.  For getting the CAN modules working, I used an Arduino library found here.  You will want to download the repo as a ZIP file and then install it within the Arduino IDE.  For help with installing this library, look here.

3.  We’ll now program one Arduino at a time.  You can actually have both of them connected to your computer but it’s easy to program the wrong Arduino, so I plugged both of them into a USB hub and only powered up one at a time.

4.  For the Transmitter Arduino, open up the Arduino IDE, and select File=Examples=CAN_Bus_Shield-master=send.  Selecting CAN Send library file

5.  I made few changes by setting the SPI_CS_PIN to 10, CAN speed to 100kbps, and setting the transmitter address to 0x11. The complete and changed code is below;

// demo: CAN-BUS Shield, send data
// loovee@seeed.cc

#include
#include

// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 10;

MCP_CAN CAN(SPI_CS_PIN); // Set CS pin

void setup()
{
Serial.begin(115200);

while (CAN_OK != CAN.begin(CAN_100KBPS))
{
Serial.println(“CAN BUS Shield init fail”);
Serial.println(” Init CAN BUS Shield again”);
delay(1000);
}
Serial.println(“CAN BUS Shield init ok!”);
}

unsigned char stmp[8] = {‘a’, ‘a’, ‘a’, ‘a’, ‘a’, ‘a’, ‘a’, ‘a’};

void loop()
{
CAN.sendMsgBuf(0x11, 0, 8, stmp);
Serial.println(“sent”);
delay(2000);
}

6.  Please notice that I changed the speed down to 100kbps from 500kbps.  But our work isn’t complete yet.

7.  Before we can fire up this code, we need to do some tuning of our CAN transmitter.  For background information, look here, toward the bottom sectioned APIs.

a.  Open your default Arduino folder, and then open the libraries folder inside it.  

b.  Open CAN_BUS_Shield-master folder.

c.  Open a file called mcp_can_dfs.h in a text editor.  It’s a long file.  But go to around line 261, and there will be three lines of code;

#define MCP_16MHz_100kBPS_CFG1 (0x03)
#define MCP_16MHz_100kBPS_CFG1 (0xBE)
#define MCP_16MHz_100kBPS_CFG1 (0x03)

d.  And we will modify it to make it look like this;

#define MCP_16MHz_100kBPS_CFG1 (0x01) //0x03
#define MCP_16MHz_100kBPS_CFG2 (0xBE) //0xFA
#define MCP_16MHz_100kBPS_CFG3 (0x03) //0x87

e.  The three hexadecimal values are very important for getting the CAN transceiver to properly work.  The next post will explain this magic.  But we move on for now.

f.  Connect the transmitter unit to power up.  Once the file is saved, return to the Arduino IDE.  From the Tools menu, select Arduino Nano as the board.  Then press the arrow button on top left corner of the IDE to compile and upload the program.

g.  If you open the Serial Monitor, you may see the output like below.  Don’t worry if you don’t see it yet, as we are not done setting up the receiver:

CAN Transmitter sending

8.  For CAN receiver, it’s fairly similar to what we just did for the transmitter.  Open up the Arduino IDE, and select File=Examples=CAN_Bus_Shield-master= receive_check.

9.  Again we change the CS pin of the SPI bus to digital pin 10, CAN bus speed to 100kbps, and change the loop timing of the setup.  The full code is changed as below;

// demo: CAN-BUS Shield, receive data with check mode
// send data coming to fast, such as less than 10ms, you can use this way
// loovee, 2014-6-13

#include
#include

#include 

const int RX_SPI0_CS_PIN = 10;

MCP_CAN CAN(RX_SPI0_CS_PIN);                    // Set CS pin

void setup()
{
   Serial.begin(115200);
   while (CAN_OK != CAN.begin(CAN_100KBPS))   // init can bus : baudrate = 100k
   {
       Serial.println(“CAN RX BUS Shield init fail”);
       Serial.println(” Init CAN BUS Shield again”);
       delay(2000);
   }
   Serial.println(“CAN RX BUS Shield init ok!”);    
}

void loop()

{
   unsigned char len = 0;
   unsigned char buf[16];
   if(CAN_MSGAVAIL == CAN.checkReceive())            // check if data coming
   {
       CAN.readMsgBuf(&len, buf);    // read data,  len: data length, buf: data buf
       unsigned int canId = CAN.getCanId();
       Serial.println(“—————————–“);
       Serial.print(“Get data from ID: “);
       Serial.println(canId, HEX);

       for(int i = 0; i<len; i++)    // print the data
       {
           Serial.print(buf[i], HEX);
           Serial.print(“\t”);
       }
       Serial.println();
   }
   delay(100);
}

10.  Power up the Pro Mini by connecting the USB-serial adapter to the PC.  This should power up the adapter and the Pro Mini.  Now I unplug the Nano (transmitter) and only have the Pro Mini (receiver) connected so that there’s only one USB port to choose from the Tools=Ports option of the Arduino IDE.  Also select the Arduino Pro Mini as the correct board, again from the Tools option.

Here’s a picture of the CAN module powered on:

CAN module powered-on

11.  Press the arrow button again to compile and upload the program, this time onto the Pro Mini.  If you are duplicating this setup, you will see LEDs blinking on the USB-serial adapter indicating the uploading process.

12.  Once the program is uploaded, connect and power up the Nano (transmitter) and open the Serial Monitor from the IDE.  You may see something like this if things go right;

Receiving CAN transmission

13.  As you can see, the Pro Mini (receiver) is receiving the transmission from the Nano indicating address 0x11 of the Nano, as well as 8 bytes of 0x61 as the payload. Yay!

14.  Well, what if it didn’t?  Or are we all done?  Actually no.  I left an important part out until the end so that you will remember (as well as I).  

15.  Lastly, take two more female-female jumper wires (or better yet jumpers) and close up two pins standing up marked J2, near the smaller JTA1050 CAN transceiver chip.  This creates proper ‘terminations’ for both end of our CAN transmission lines, as they are required.  Each end of CAN lines require termination with 120 Ohm resistors.  More information this, look here under ISO 11898-2 section for high speed CAN termination.

My final setup looks like this below:  On the left side of the breadboard is the Arduino Nano transmitter with its CAN module above it.  On the right side of the breadboard are the Arduino Pro Mini receiver, USB-Serial adapter and the CAN module above it.  The wires arcing (but not sparking) on top of the photo are the High and Low CAN lines that are actually carrying the signal.

All this work just to send some bits across 25cm!  Theoretically though, at 100kbps, the maximum bus length as well as node distance according to this page is 500m, which is pretty impressive.

CAN Bus Setup

Bye for now

So as you can see, there are quite a few steps to take, but we finally got some data coming over from one CAN node to another.  Here’s what actual ones and zeros of that transmission look like seen through a logic analyzer:

Full decoded CAN frame

Notice the bitrate of that transmission is 100000 (or 100k) bps in my decoder setting.  At the beginning of the data is a symbol S indicating the Start-of-frame bit that signals the beginning of a transmission.  The ID field indicates 0x11, which is what our transmitter intended.  And those eight green fields in the middle labeled DB containing the value of 0x61.  Each of those DB is a Data Byte and looks to match what we sent out.  

However, there are a lot of magic left out that I have yet to explain, and I will discuss them more in the next post.  In the meantime, enjoy your new CAN bus!

By |2018-09-04T10:01:46+00:0031/08/2017|Categories: Coding, Education|Tags: , , |Comments Off on Say it with a CAN Bus and a Raspberry Pi

About the Author:

A driven connoisseur of electron flow and all that consumes them. "If you didn't get it, you didn't want it that bad"