Command-line tutorial
The first tutorial does not require any programming. We will use the CANopen control tool to create CANopen master and slave processes that communicate over a virtual CAN bus. No CAN hardware is required; instead, we will use the virtual CAN interface provided by SocketCAN.
Note: Using SocketCAN means this tutorial only works on Linux.
You may want to monitor traffic on the virtual CAN bus, to better understand
what is going on. In this tutorial we will assume the use of candump
from
can-utils, but
Wireshark works just as well.
Virtual CAN interface
Make sure the virtual CAN driver is loaded:
sudo modprobe vcan
and create a virtual CAN network interface with the name vcan
:
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0
You can check that the interface was created by running
ip link show vcan0
This should output something like
3: vcan0: <NOARP,UP,LOWER_UP> mtu 72 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/can
To monitor the CAN traffic in this tutorial, run
candump vcan0
in a terminal window and keep it open. You can also capture the traffic from
vcan0
with Wireshark.
A single device
Every CANopen device, including the mock master and slave in this tutorial, has an object dictionary. The layout and content of this object dictionary is described in an electronic data sheet (EDS) or device configuration file (DCF).
“EDS” and “DCF” tend to be used interchangeably. Technically speaking, an EDS describes a certain type of device, while a DCF describes an individual device, including unique values such as a serial number. But most tools and parsers do not make a distinction.
Open a terminal and create a virtual CANopen device with the default DCF for the CANopen control tool:
coctl vcan0 /etc/coctl.dcf
Note: If you installed Lely CANopen from source, coctl.dcf
might be in a
different directory, such as /usr/local/etc
.
Initialize the device (input is shown beginning with >
, output with <
):
> [1] set network 1 # Set the default network-ID, so it can be omitted.
< [1] OK
> [2] set id 1 # Set the node-ID of this device to 1.
< [2] OK
> [3] init 0 # Initialize the device, and configure the CAN bus with
> [3] # with a bit rate of 1000 kbit/s.
< coctl: error: unable to set bitrate of vcan0 to 1000000 bit/s
< coctl: NMT: entering reset application state
< coctl: NMT: entering reset communication state
< coctl: NMT: running as master
< coctl: NMT: entering pre-operational state
< coctl: NMT: entering operational state
< [3] OK
The vcan
driver does not support changing the bitrate. This is an error you
can safely ignore.
The terminal running candump
shows two new lines:
vcan0 701 [1] 00
vcan0 000 [2] 82 00
The first line is the boot-up message from our device. Heartbeat and boot-up
messages always have CAN-ID 0x700+$NODEID
and a single-byte payload with the
current state. 00
indicates the device just rebooted.
The second line is there because our device is configured as a CANopen master;
it sent the network management (NMT) command (CAN-ID 000
) “reset
communication” (82
) to all nodes (00
) the moment it went operational. If
other nodes where present, this command would be followed by their boot-up
messages.
Adding a slave
Download cmd-slave.dcf, open a terminal in the directory where you stored the file and run
coctl vcan0 ./cmd-slave.dcf
Initialize the slave with node-ID 2:
> [1] set network 1
< [1] OK
> [2] set id 2
< [2] OK
> [3] init 0
< coctl: error: unable to set bitrate of vcan0 to 1000000 bit/s
< coctl: NMT: entering reset application state
< coctl: NMT: entering reset communication state
< coctl: NMT: running as slave
< coctl: NMT: entering pre-operational state
< coctl: NMT: entering operational state
< [3] OK
candump
shows a new line with the boot-up message:
vcan0 702 [1] 00
The boot-up message is detected by the master, which you can see by pressing
<Enter>
in the terminal of the first device:
> [4]
< 1 2 BOOT_UP
< 1 2 USER BOOT A (The CANopen device is not listed in object 1F81.)
Sending heartbeats
Neither the master or the slave are configured to produce heartbeat messages. From the master we can configure the heartbeat producer on the slave (node-ID 2) by using a service data object (SDO) request to write the number of milliseconds (as a 16-bit unsigned integer) to object 1017 sub-index 0 (Producer heartbeat time):
> [4] 2 write 0x1017 0 u16 1000
< [4] OK
The SDO request shows up in the candump
output as two lines:
vcan0 602 [8] 2B 17 10 00 E8 03 00 00
vcan0 582 [8] 60 17 10 00 00 00 00 00
The first frame is the expedited SDO download (= write) request from the master. Because the value (0x03e8 = 1000) is smaller than 4 bytes, the entire request fits in a single frame. The second frame is the response from the slave, acknowledging that the value was written to the object dictionary.
From now on, candump
shows the following frame every second:
vcan0 702 [1] 05
05
means the slave is in the NMT state “operational”.
We can also read the value back from the master:
> [5] 2 read 0x1017 0 u16
< [5] 0x03e8
candump
shows the request and reponse:
vcan0 602 [8] 40 17 10 00 00 00 00 00
vcan0 582 [8] 4B 17 10 00 E8 03 00 00
LSS
CANopen nodes are uniquely identified by a combination of four 32-bit numbers: the vendor-ID, product code, revision number and serial number. The identity values are stored in object 1018 sub-index 1-4, respectively:
> [6] 2 r 0x1018 1 u32
< [6] 0x00000360
> [7] 2 r 0x1018 2 u32
< [7] 0x00000000
> [8] 2 r 0x1018 3 u32
< [8] 0x00000000
> [9] 2 r 0x1018 4 u32
< [9] 0x00000000
The vendor-ID is 0x360 (registered by Lely Industries N.V.) and the product code, revision number and serial number are all 0.
Detecting nodes
If we don’t know the node-ID of a slave (or it is unconfigured), we can discover it by using the layer setting services (LSS).
The following (Lely-specific) command scans the network for nodes:
> [10] _lss_fastscan 0 0 0 0 0 0 0 0
The eight zeros are our initial guesses for the four identity values and the masks specifying which bits are already known (in this case none).
For every one of the 128 bits, the master sends a request (CAN-ID 7E5
) asking
if there is a node which matches the current (masked) value, first trying 0,
then 1. It waits for 100 ms for one or more nodes to reply (CAN-ID 7E4
). Since
most of the bits are zero for our slave, the scan takes a little over 13
seconds.
The result is
< [10] 0x00000360 0x00000000 0x00000000 0x00000000
which matches the values obtained earlier.
There also exists an _lss_slowscan
method for devices that don’t support the
LSS Fastscan service. It requires the user to provide the vendor-ID and product
code, and a range of revision numbers and serial numbers. It then uses a binary
search to narrow down those last two numbers.
Changing the node-ID
The node we just identified is now in the “configuration” state and will respond to LSS configuration requests. Any other node in the network will ignore those requests since it is still in the “waiting” state.
We can ask for the node-ID:
> [11] lss_get_node
< [11] 2
and even change it:
> [12] lss_set_node 3
> [12] OK
But this does not yet have any effect:
> [13] lss_get_node
< [13] 2
The new node-ID is still “pending”. First, switch the node back to the “waiting” state:
> [14] lss_switch_glob 0
< [14] OK
Further LSS configuration requests will now fail:
> [15] lss_get_node
> [15] ERROR: 103 (Time-out)
Normally we would first use lss_store
to request the node to store the new
node-ID to non-volatile memory so it survives a reboot, but the virtual slave
created by the CANopen control tool does not support this.
To activate the new node-ID, we issue the NMT “reset communication” command to node 2:
> [16] 2 reset comm
< [16] OK
< 1 3 BOOT_UP
< 1 3 USER BOOT A (The CANopen device is not listed in object 1F81.)
and receive a boot-up message from node 3:
vcan0 000 [2] 82 02
vcan0 703 [1] 00
Note that the slave is no longer sending heartbeat messages. The “reset communication” command caused objects 1000..1FFF, which includes 1017 (Producer heartbeat time), to be reset to their default values.
Sending and receiving PDOs
During normal operation, real-time data transfer between nodes is performed by means of the process data object (PDO) service. The transmission of a PDO can be event-driven or synchronous. In the latter case, the master periodically sends a synchronization object (SYNC) to trigger the transmission, reception and processing of PDOs.
To start transmitting SYNC messages, write the period between messages (in µs) to object 1006 sub-index 0 (Communication cycle period) in the local object dictionary of the master:
> [17] 1 w 0x1006 0 u32 1000000
< [17] OK
From now on, the following message shows up every second:
vcan0 080 [0]
No PDOs are sent after the SYNC, because they have not been configured yet.
Go to the terminal running the CANopen control tool for the slave and configure a Transmit-PDO (TPDO) with
> [4] set tpdo 1 0x183 sync1 1 0x2007 0 u32
< [4] OK
This tells the slave to create the first TPDO (1
), with the default CAN-ID
(0x180+$NODEID
), that is sent on every SYNC (sync1
) and contains a single
object (1
). The object mapped into the PDO is 0x2007 sub-index 0, which is a
32-bit unsigned integer (u32
).
Once the TPDO is configured, it is sent after every SYNC:
vcan0 080 [0]
vcan0 183 [4] 00 00 00 00
To see that it really contains the mapped object, change the value of object 2007 in the local object dictionary:
> [5] 3 w 0x2007 0 u32 0x12345678
< [5] OK
Starting with the next SYNC, the new value appears in the payload of the CAN frame (in little-endian):
vcan0 080 [0]
vcan0 183 [4] 78 56 34 12
Although a PDO is now sent on every SYNC, it is not yet received, because no node has configured a corresponding Receive-PDO (RPDO).
Go back to the terminal running the CANopen control tool for the master and configure the first RPDO with
> [17] set rpdo 1 0x183 sync1 1 0x2007 0 u32
< [17] OK
The parameters are the same as for the TPDO on the slave. The CAN-ID and
transmission type must be equal, but the object mapping only has to be
compatible. However, in this case the master also happens to have an object in
the local object dictionary with index 2007, sub-index 0 and type u32
, so the
mapping is equal as well.
You can check that the master is now receiving the PDOs by pressing <Enter>
:
> [18]
< 1 pdo 1 1 0x12345678
< ...
and that it stores the value of the PDO in the local object dictionary:
> [18] 1 r 0x2007 0 u32
< 1 pdo 1 1 0x12345678
< ...
< [18] 0x12345678
That concludes the command-line tutorial.