Custom Vehicle Signals

The Vehicle Signal Specification has a lot of standard Vehicle Signals already defined.

However, if you want to add additional custom signals or mappings from CAN-Bus for the Kuksa.VAL dbc2val feeder, the VSS data model needs to be customized and regenerated.

The following steps can be performed on a development machine to regenerate VSS data model with custom a vspec mapping.

  1. Concepts
  2. Setup workspace
  3. Defining the VSS Overlay
  4. Regenerate VSpec JSON
  5. Generate a candump
  6. Databroker and dbc2val containers
  7. Testing with data

Concepts

VSS Overlay

The VSS Overlay concept allows to override the standard signals in the specification with custom signals. It defines metadata attributes like signal name, datatype, description etc.

In addition, the vspec file allows to add custom metadata, such as the dbc node in the following example:

Vehicle.Speed:
  type: sensor
  datatype: float
  dbc:
    message: Kombi_01
    signal: KBI_angez_Geschw
    interval_ms: 1000

See Eclipse Kuksa documentation about dbc2val mapping for details.

There are also ways to automatically transform the CAN-Bus signal values into VSS values:

  • Mapping of literal values, such as enums and booleans
  • Mathematical transformations
  • Full and partial transformations

Setup workspace

Required tools

  • Git
  • Docker
  • Python 3
  • can-utils
  • a YAML/JSON text editor

Depending on your host OS, you may need to install the packages first:

sudo apt-get update
sudo apt-get install can-utils git docker python3

Workspace structure

You need the following files in your workspace folder:

  • ./custom.dbc - The vehicle-specific DBC file. You can use the Tesla Model 3 example or check out the opendbc project.
  • ./custom.vspec - The VSS overlay from the previous step

Defining the VSS Overlay

The definition of an VSS overlay is a manual process. It requires research, browsing the existing VSS catalog and deep knowledge of the vehicle signals. Especially knowing the semantic of a signal and how to map it between DBC and VSS is a complex task.

Note: The Leda team is not aware of any advanced tooling to support the automation of this task.

  1. Create a new file custom.vspec. Use YAML format
  2. For each identified signal:
    1. Look up if there already is a corresponding signal in VSS. You may use the Digital.Auto Playground or the spec directly.
    2. Add a new top-level entry to the custom.vspec file. The key is the full VSS path name in dot notation.
    3. Add the corresponding dbc node
    4. Add the signal attribute and enter the name of the CAN-Bus signal name in the DBC
    5. Add additional attributes such as interval_ms, message or transform.

      Note: The attributes signal, interval_ms and transform are evaluated by dbc2val. The attribute message is only evaluated by the below demo script and is optional.

Example:

# CAN Message: Kombi_01
Vehicle.Speed:
  type: sensor
  datatype: float
  dbc:
    message: Kombi_01
    signal: KBI_angez_Geschw
    interval_ms: 1000

# CAN Message: FS_Position_01
Vehicle.Cabin.Seat.Row1.Pos1.Position:
  type: sensor
  datatype: float
  dbc:
    message: FS_Position_01
    signal: FSS_Laenge_Pos
    interval_ms: 1000

These two extensions will be added to the combined VSS tree:

VSS Overlay with custom CAN-Bus metadata

Regenerating VSpec

The script vspec2json.py from COVESA VSS Tools can then be used to merge the overlay file with the standard specification.

For Leda devices, you can use kanto-cm to run the tool as a container:

# Copy custom.dbc and custom.vspec to /home/root
# Run in /home/root
kanto-cm create --name vspec2json --rp no --mp="/home/root:/data" ghcr.io/eclipse-leda/leda-vss-vspec2json:main
kanto-cm start --a --i --name vspec2json

For developer workstations, there is a convenient pre-built Docker container:

docker run --name vspec2json --rm -v `pwd`:/data ghcr.io/eclipse-leda/leda-vss-vspec2json:main

Or, if you want to run vspec2json from the sources, clone the repository and invoke it like this:

git clone \
    --recurse-submodules \
    --branch v3.1 \
    --single-branch \
    --depth 1 \
    https://github.com/COVESA/vehicle_signal_specification

python3 vehicle_signal_specification/vss-tools/vspec2json.py \
    -e dbc \
    -o custom.vspec \
    --uuid \
    --json-pretty \
    --json-all-extended-attributes \
    ../vehicle_signal_specification/spec/VehicleSignalSpecification.vspec \
    custom_vss_dbc.json

With the resulting file custom_vss_dbc.json and the corresponding DBC file, the dbc2val feeder can relay the signals to the databroker. dbc2val can either read from a CAN-Dump file (generated by candump from can-utils) in an endless loop, or can listen on actual virtual or physical CAN interfaces.

The output should look like this:

$ ls
custom.dbc  custom.vspec
$ docker run --name vspec2json --rm -v `pwd`:/data ghcr.io/eclipse-leda/leda-vss-vspec2json:main
INFO     Output to json format
INFO     Known extended attributes: dbc
INFO     Added 54 units from /vss/spec/units.yaml
INFO     Loading vspec from /vss/spec/VehicleSignalSpecification.vspec...
INFO     Applying VSS overlay from /data/custom.vspec...
INFO     Autocreating implicit branch Vehicle
INFO     Autocreating implicit branch Cabin
INFO     Autocreating implicit branch Seat
INFO     Autocreating implicit branch Row1
INFO     Autocreating implicit branch Pos1
INFO     Calling exporter...
INFO     Generating JSON output...
INFO     Serializing pretty JSON...
INFO     All done.
$ ls
custom.dbc  custom.vspec  custom_vss_dbc.json

Databroker Container

Ensure the databroker container is up and running:

On developer workstation:

docker run --name databroker --detach --rm -p 55555:55555/tcp ghcr.io/eclipse/kuksa.val/databroker:0.3.1

On Leda device (if not already running)

kanto-cm start --name databroker

Testing with data

To see actual data, we need to use the candump or another CAN source and feed that data into the databroker. After that is running, we can use the databroker-cli tool to subscribe to the example VSS signals.

Generate a candump

To generate a candump for testing purposes, start the candump tool and listen for some packets.

In the following example, the first 20 CAN frames are being recorded and the command will exit.

candump -L -n 20 vcan0 > candump.log

If you do not have an external CAN source, you may want to simulate CAN frames. There is a Python library for parsing DBC files, which also let’s you encode and send the actual CAN messages.

An example script could look like this:

Note: Due to GPL-3 licensing, the Leda quickstart image does not contain some Python standard libraries. This script will not run on a Leda quickstart image.

import random
import time
from pprint import pprint

import yaml
import cantools
import can

can_bus = can.interface.Bus('vcan0', bustype='socketcan')
db = cantools.database.load_file('custom.dbc')
with open("custom.vspec", "r") as stream:
    try:
        vspec = yaml.safe_load(stream)

        for key in vspec:
            message_name=vspec[key]['dbc']['message']
            signal_name=vspec[key]['dbc']['signal']
            example_message = db.get_message_by_name(message_name)

            # Initialize an empty message
            data = {}
            for signal in example_message.signals:
                data[signal.name] = False

            pprint("Message: %s" % example_message)

            # Send 10 frames with random data
            for x in range(10):
                data[signal_name] = random.uniform(0,100)
                encoded_data = example_message.encode(data)
                message = can.Message(arbitration_id=example_message.frame_id, data=encoded_data)
                pprint("CAN Frame: %s" % message)
                can_bus.send(message)
                # Wait up to 1 second
                time.sleep(random.random())

    except yaml.YAMLError as exc:
        print(exc)

Example output:

"Message: message('Kombi_01', 0x30b, False, 8, {None: 'MQB'})"
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 b9 00')
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 16 00')
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 ca 00')
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 ad 00')
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 b0 00')
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 79 00')
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 1b 01')
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 19 01')
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 0a 00')
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 b0 00')
"Message: message('FS_Position_01', 0x6be, False, 8, None)"
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    59 00 00 00 00 00 00 00')
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    31 00 00 00 00 00 00 00')
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    29 00 00 00 00 00 00 00')
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    07 00 00 00 00 00 00 00')
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    40 00 00 00 00 00 00 00')
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    55 00 00 00 00 00 00 00')
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    54 00 00 00 00 00 00 00')
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    07 00 00 00 00 00 00 00')
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    62 00 00 00 00 00 00 00')
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    59 00 00 00 00 00 00 00')

dbc2val on workstation

Get the latest sources:

git clone https://github.com/eclipse/kuksa.val.feeders

Looping a CAN dump recording:

kuksa.val.feeders/dbc2val/dbcfeeder.py \
    --dumpfile candump.log \
    --canport vcan0 \
    --server-type kuksa_databroker \
    --dbcfile custom.dbc \
    --mapping custom_vss_dbc.json

Listening on CAN interface:

# CAN must be set up up front, e.g.
# modprobe vcan
# ip link add dev vcan0 type vcan
# ip link set vcan0 up

kuksa.val.feeders/dbc2val/dbcfeeder.py \
    --canport vcan0 \
    --use-socketcan \
    --server-type kuksa_databroker \
    --dbcfile /custom/custom.dbc \
    --mapping /custom/custom_vss_dbc.json

dbc2val on Leda device

To run the dbc2val feeder on Leda, you can use kanto-cm (instead of docker) to create the respective containers.

Note: Due to Issue #73, the container ghcr.io/eclipse/kuksa.val.feeders/dbc2val:v0.1.1 cannot be used with SocketCAN. Please use a later release or build the container from the sources after fixing the bug. Alternatively, use the candump file instead of SocketCAN.

  1. Remove any existing dbc2val container:

    kanto-cm remove --force --name dbc2val
    
  2. Create the new container in Kanto Container Management:

    kanto-cm create \
        --name dbc2val \
        --mp="/home/root:/data" \
        --e=VDB_ADDRESS="databroker:30555" \
        --e=USECASE="databroker" \
        --network=host \
        --privileged \
        ghcr.io/eclipse/kuksa.val.feeders/dbc2val:v0.1.1 \
        /dist/dbcfeeder-exe \
        --canport vcan0 \
        --use-socketcan \
        --usecase databroker \
        --dbcfile /data/custom.dbc \
        --mapping /data/custom_vss_dbc.json
    

    Note: The host networking and privileged is used to allow the container access to the CAN sockets.

  3. Start the container:

    kanto-cm start --a --i --name dbc2val
    
  4. Check the logs to verify container is running:

    kanto-cm logs --name dbc2val
    

Querying via CLI

To be able to see the changes, you may want to use the Kuksa Databroker CLI:

docker run --rm -it --network host ghcr.io/eclipse/kuksa.val/databroker-cli:0.3.1

Now, depending on the way how the dbc2val feeder has been started (candump vs. socketcan), you will either already see data from the candump loop.

Eclipse Kuksa.VAL Databroker CLI