Skip to content

How to set interact with a UAVCAN node with Raspberry Pi and CAN Hat or BeagleBone

generationmake edited this page Jan 31, 2021 · 1 revision

CAN Interface

The Raspberry Pi has no builtin CAN interface. So a device needs to be added. For the first tests a virtual CAN interface might be OK. But this is not sufficient for a connection with real hardware.

UAVCAN uses the Socket CAN interface on linux. CAN devices can be listed by typing ifconfig.

Bitrate depends on your hardware. Typical value is 250k.

Virtual CAN port

The virtual CAN port is already compiled into the default raspbian kernel. It only needs to be enabled.

sudo modprobe vcan
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0

Raspberry Pi with Microchip MCP2515

The MCP2515 is a CAN controller which connects to the SPI port. Several HATs and breakout boards are available.

edit /boot/config.txtand add line:

dtoverlay=mcp2515-can0,oscillator=16000000,interrupt=25

values of oscillator and interrupt depend on your hardware.

after each boot enable can interface with:

sudo ip link set can0 up type can bitrate 250000

Raspberry Pi with Microchip MCP2517FD or MCP2518FD

The MCP2518FD is a CAN FD controller with higher data rata. The MCP2517FD is its predecessor and supports also CAN FD but is not recommended for new designs. Both connect via SPI.

edit /boot/config.txt and add line:

dtoverlay=mcp251xfd,spi0-0,interrupt=25

keep care that you choose the right interrupt and SPI CE line and populate the corresponding resistor.

enable CAN after boot up with

sudo ip link set can0 up type can bitrate 250000

BeagleBone

Unlike the Raspberry Pi the BeagleBone has two builtin CAN controller. You only have to connect a CAN transceiver like TI SN65HVD232. The latest distributions (Debian Buster 10.x) have the CAN controller already enabled in the kernel. You only have to switch the IO pins to CAN mode.

Usually CAN1 is selected because CAN0 interferes with I2C2 which is used to identify capes. CAN1 is routed to pins P9.24 (RX) and P9.26 (TX). Connect the CAN transceiver to these pins. The pins have to be enabled with:

sudo config-pin p9.24 can and sudo config-pin p9.26 can

This blog post shows how to automate this: https://www.bacpeters.com/2020/01/25/configuring-the-beaglebone-black-gpio-pins-permanently/

Then the CAN interface has to be enable like on the Raspberry Pi after each boot up:

sudo ip link set can1 up type can bitrate 250000

Attention: On the BeagleBone can1 is used. So replace can0 with can1 in all following examples.

Also the BeagleBone has less computing power than the Raspberry Pi. Therefore every action will take way longer than on the Raspberry Pi.

Testing the interface

The CAN interface can be tested with the can-utils. Install the with:

sudo apt-get install can-utils

all messages on the CAN bus can be displayed using

candump -decaxta vcan0 or candump -decaxta can0

Attention: The physical CAN bus only works correctly if there are at least two active devices connected to the bus. Otherwise Socket-CAN might report: [Errno 105] No buffer space available.

Install yakut

  • Install libatlas sudo apt-get install libatlas-base-dev

  • python3 is already installed, link to python 3

  • check pip3 version with pip3 --version pip should be 20.3.3 or newer.

  • update pip if necessary pip3 install --upgrade pip

  • install yukat with pip3 install yakut

This should install yukat in /home/pi/.local/bin.

To access pyuavcan easily the path should be added to PATH. On raspbian this is done by default. Otherwise update $PATH in .profile and add the line:

$PATH="$PATH:/home/pi/.local/bin"

Check if yakut installation was successful by typing yakut --version This should print the version of yakut.

Generate DSDL with the standard namespaces:

cd ~
yakut compile https://github.com/UAVCAN/public_regulated_data_types/archive/master.zip

This may take a few minutes.

Send test string:

yakut --transport='CAN(can.media.socketcan.SocketCANMedia("vcan0",8),59)' publish uavcan.diagnostic.Record.1.0 '{"text": "Hello world!"}'

59 is the Node-ID and can be changed to any desired value. Node-IDs need to be unique on the system.

The text string should be visable in a second window running candump.

Now the installation of yakut is finished.

Connect to Arduino Board

Use an Arduino with CAN interface, for example an Arduino MKR Zero with an MKR CAN Shield. Connect the CAN interface of the Raspberry Pi to the one of the Arduino. Connect CANH to CANH and CANL to CANL and GND to GND. Connection of VCC is not necessary and not recommended. Take care of the termination resistor. In the CAN spec it is defined that on both ends of a CAN line a 120 ohm termination should be installed. When you use only a short line with only a few devices connected the correct value of this resistor is not that important. But at least on resistor should be placed on the line.

Load a uavcan sketch onto the Arduino, for example the UAVCAN-Heartbeat-Publish example.

Attention: When running the arduino sketch unattended, comment the initialisation of the serial port. Otherwise the sketch will wait forever at startup and no CAN messages will be sent. Change the following line:

-  while(!Serial) { }
+//  while(!Serial) { }

How do debug

candump

The messages can be received by the Raspberry Pi and displayed using candump. Type:

candump -decaxta can0

The output (only heartbeat) should be like:

 (1608484588.783110)  can0  RX - -  107D550D   [8]  F0 0F 00 00 00 00 00 EE   '........'
 (1608484589.786750)  can0  RX - -  107D550D   [8]  F1 0F 00 00 00 00 00 EF   '........'
 (1608484590.790368)  can0  RX - -  107D550D   [8]  F2 0F 00 00 00 00 00 F0   '........'
 (1608484591.794008)  can0  RX - -  107D550D   [8]  F3 0F 00 00 00 00 00 F1   '........'
 (1608484592.797639)  can0  RX - -  107D550D   [8]  F4 0F 00 00 00 00 00 F2   '........'
 (1608484593.801287)  can0  RX - -  107D550D   [8]  F5 0F 00 00 00 00 00 F3   '........'

yakut

The messages can also be displayed by pyuavcan. At the moment only defined messages like the Heartbeatcan be received:

yakut --transport='CAN(can.media.socketcan.SocketCANMedia("can0",8),59)' sub uavcan.node.Heartbeat.1.0

output:

---
7509:
  _metadata_:
    timestamp:
      system: 1608484435.212532
      monotonic: 7463.789214
    priority: nominal
    transfer_id: 16
    source_node_id: 13
  uptime: 3926
  health:
    value: 0
  mode:
    value: 0
  vendor_specific_status_code: 0

---
7509:
  _metadata_:
    timestamp:
      system: 1608484436.216082
      monotonic: 7464.792549
    priority: nominal
    transfer_id: 17
    source_node_id: 13
  uptime: 3927
  health:
    value: 0
  mode:
    value: 0
  vendor_specific_status_code: 0