CAN Codec¶
The CAN codec is included in the zeloscloud
package, and requires extras can
to be installed.
Introduction¶
The CAN codec is designed to streamline communication over CAN interfaces. It simplifies interactions with CAN buses for operations like transmitting and/or receiving CAN messages via signal/message objects. https://en.wikipedia.org/wiki/CAN_bus
The key components are:
- CanCodec: Manages the encoding and decoding of data sent over a CAN link.
- CanLink: Represents the physical connection over which data is transmitted.
- CanMessage: Abstractions for messages
- CanSignal: Abstractions for signals
Read these!
Checkout the CAN link documentation for additional context can link.
How the Codec Works¶
The CanCodec
operates by generating a set of predefined messages/signals from a configuration file like a DBC.
Instances of messages and signals are represented by the CanMessage
and CanSignal
classes respectively.
When you initialize the codec, you provide a configuration that defines the set of messages and signals for a given bus.
Quick Start Guide¶
This section will walk through creating and using a CanCodec
, a CanLink
, and the corresponding Signals
/ Messages
import can
from zeloscloud.links.can_link import CanLink
from zeloscloud.codecs.can import CanCodec
link_config = {
"channel": "vcan0", # Or the name of a 'real' CAN dongle
"interface": "virtual", # Or 'socketcan' if using Linux + CAN dongle
}
codec_config = {
"allow_duplicates": True, # Allow messages/signals with the same name
"suppress_errors": True, # Suppress error (messages) when CAN bus errors and/or unknown frames are encountered.
}
dbc = "/path/to/my.dbc"
with CanLink(name="can-link", config=link_config) as link, \
CanCodec(name="can-codec", config=codec_config, link=link, dbc=dbc) as codec:
# Send raw messages:
message = can.Message()
link.send(message)
# Send named messages defined in your DBC:
codec.MyMessage.MySignal.set(123.0)
codec.MyMessage.send_periodic()
Basic Pytest Setup¶
To set up a basic codec, you need to initialize it with a name, link, and configuration file. Here's a quick example to get you started:
conftest.py¶
import pytest
from zeloscloud.codecs.can import CanCodec
from zeloscloud.links.can_link import CanLink
@pytest.fixture(scope="session")
def link():
"""Create a CAN link that provides message send/recv capability"""
config = {
"channel": "vcan0", # Or the name of a 'real' CAN dongle
"interface": "virtual", # Or 'socketcan' if using Linux + CAN dongle
}
with CanLink(name="vcan0-link", config=config) as link:
yield link
@pytest.fixture(scope="session")
def codec(link):
"""Create a CAN codec that monitors the CAN bus and encodes/decodes frames into messages/signals"""
with CanCodec(name="vcan0-codec", link=link, dbc="/path/to/file.dbc") as codec:
yield codec
Pytest Fixtures
We are utilizing pytest fixtures to inject dependencies into our tests and setups. This method ensures that our components are modular and can be tested independently. The can_link
and can_codec
fixtures can also be used as standalone components.
Codec Configuration¶
@pytest.fixture(scope="session")
def codec(link):
"""Create a CAN codec that monitors the CAN bus and encodes/decodes frames into messages/signals"""
with CanCodec(name="vcan0-codec", link=link, dbc=/path/to/file.dbc) as codec:
yield codec
Configuration Details
The CanCodec accepts a config
parameter which is a dictionary that enables / disables certain features. The default configuration is:
- allow_duplicates: False : raise errors when there are duplicate signal / message names.
- suppress_errors: True : suppress error (messages) when CAN bus errors and/or unknown frames are encountered.
- emit_schema: False : emit the CAN schema for all messages/signals on init
Example Usage¶
After setting up the codec with the configuration, you can use it in your application to send and receive data. Here's an example showing how to interact with the CanCodec
.
Using the Codec in an Application¶
import time
def test_off_to_on(codec, check):
"""
Below example test assumes:
- Device on the CAN bus named DUT (Design Under Test)
- DUT sends a `Status` message periodically
- `Status` message has a State signal
- State signal can be "OFF" or "ON"
- DUT accepts a `Command` message
- `Command` message has a TargetState signal
- When the TargetState signal = ON the DUT should move to it's ON state
"""
# Ensure that the DUT starts the test in OFF
check.that(codec.Status.State, '=', "OFF")
# Send the ON `Command` message periodically
codec.Command.TargetState.set("ON")
codec.Command.send_periodic()
# Wait for the DUT to transition to "ON" and send an updated Status message
time.sleep(0.2)
# Ensure that the DUT is now in the ON state
check.that(codec.Status.State, "=", "ON")
Using the Codec
In this example, the CanCodec
is used to turn the Device Under Test on. It also demonstrates how to use the check plugin for making assertions on the data.
Accessing Signals within Messages¶
Signals can be accessed through the following attributes:
- codec.
In the case of duplicated signal names, signals must be referenced through the encapsulating message.
codec.message_one.duplicate_signal.set(1)
codec.message_two.duplicate_signal.set(0)
In the case of duplicated message names, messages and signals must be referenced through the encapsulating message referenced by the identifier.
codec.messages_by_id[123].duplicate_signal.set(1)
codec.messages_by_id[456].duplicate_signal.set(0)
Iterating Through Codec and Message Signals¶
You can directly iterate through all signals registered with the codec
Example Usage:¶
for signal in codec:
print(f"Signal Name: {signal.name}, Signal Value: {signal.get()}")
for message in codec.signals:
print(f"Signal Name: {signal.name}, Signal Value: {signal.get()}")
for message in codec.messages:
print(f"Message Name: {message.name}, message Value: {message.get()}")
Troubleshooting¶
Permissions¶
OSError: [Errno 19] No such device
ifconfig
to check if the device (CAN bus) is present
Bring up (and configure) the CAN bus:
ip link set down can0
ip link set can0 type can bitrate 500000
ip link set can0 type can restart-ms 100
ip link set can0 txqueuelen 1000
ip link set up can0
ip -details -statistics link show can0
can0
with your actual device.
Debugging¶
cantools¶
candump can0 | python3 -m cantools decode path/to/dbc
can-utils¶
candump can0
candump can0 -t d
candump can0,123:0x7FF -t d
cangen can0
API Reference¶
See zeloscloud.codecs.can in the API Reference.