Skip to main content

Working with the CAN bus

NOTE

To run the examples here, you need a computer with support for vcan. vcan are virtual created CAN adapters which are provided with Linux socketcan and Linux can-utils.

In the chapter Working with recorded streams we described how to read recorded CAN bus signals from a file. To read from the actual CAN bus you need to do a few adjustments.

1. Import the physical CAN wrapper in SA Engine. While the system model contains the sensor ontology and parsing functions, this wrapper handles the access to the actual CAN bus on Linux.

pushd(startup_dir() + "../wrappers/sa_canbus");
load_lisp("canbus.lsp");
popd();

2. Set up your CAN channels. This depends on what CAN bus reader you have on your device, but here is how to do it for vcan0 and vcan1.

create can:channel(id, name, buffer_size, timeout) instances
(1, "vcan0", 130112, 0.5),
(2, "vcan1", 130112, 0.5);

3. Redirect the signal bus to read from the CAN bus. This is achieved by changing the CAN data source to the canbus_rx_bg functions that is proveded with the physical CAN wrapper. canbus_rx_bg will read the channels set up in step 2.

set bus(thetype("can:signal")) = #'canbus_rx_bg';

Now any call to either can:signal_bus(), can:signal_stream() or can:ts_signal_stream() will read signals from the CAN bus.

Testing

Use can-utils to send frames on the CAN bus

Linux can-utils contains command line tools to send and read frames on the CAN bus. This can help you verify that your model is actually reading from the CAN bus and that the model works as intended.

You can use cansend to send individual frames:

$ cansend vcan0 0CF00400#ABCD1234ABCD1234

You can use canplayer to both record from and send frames to the CAN bus.

To send recorded frames to the CAN bus with canplayer you run

$ cat j1939-can-data.log | canplayer vcan0=can0

which tells canplayer to send the contents of the file j139-can-data.log on the CAN bus and send frames received from can0 to vcan0, where vcan0 is a channel you have created with can:channel().

Setting up test signals

In Importing DBC files we learned how to automatically create signals based on DBC files. OSQL definitions like the following are created:

create can:signal (name, cid, fid, decoder, params, options) instances
("EEC1_EngineSpeed", 0, 0x0CF00400, #'can:unpack_scale',['Z24u16', 0.125, 0],{"min":0,"max":8031.875});

For ensuring that the CANBUS under test is being read, it might be convenient to create a simpler test signal like:

create Can:signal(name, doc, cid, fid, decoder) instances
('Test', 'my test signal', 0, 0x18F, #'can:whole');

The test signal listens for frame id 0x18F, and instead of parsing the signal with the can:unpack_scale function, it uses can:whole to show the payload. We can send test data on it with:

$ cansend vcan0 18F#ABCDABCD

We can also create a can:raw() function to read everything that is sent on the configured CAN data source, regardless of frame id.

create function can:raw() -> Stream of Vector
as select Stream of [ts, canid, integer2hex(fid), hex(payload)]
from Timeval ts, Binary payload, Integer canid, Integer fid,
Stream of Vector cs
where cs = call(bus(typenamed("can:Signal")))
and [ts, canid, fid, payload] in cs;