Cantastic

Cantastic is an Elixir library to interact with CAN/Bus via lib_socket_can (Linux only). It does all the heavy lifting of parsing the incoming frames and sending the outgoing ones at the right frequencies.

RAW and ISOTP modes are currently supported, BCM (Broadcast Manager) support is planned.

Installation

in the mix.exs file:

def deps do
  [{:cantastic, "~> 1.0.1"}]
end

OTP App Configuration

Example

config :cantastic,
  can_network_mappings: "ovcs:vcan0,leaf_drive:vcan1,polo_drive:vcan2",
  setup_can_interfaces: true,
  otp_app: :vms_core,
  priv_can_config_path: "polo_2007.yml",
  enable_socketcand: true,
  socketcand_ip_interface: "wlan0"

Description

Cantastic supports the following configuration options:

Key Description Default value
:can_network_mappings A comma separated list of can network names and related interfaces.
:setup_can_interfaces Wheher Cantastic should setup the CAN interfaces. It requires the Elixir user to have the approriate rights (usually the case for Nerves hosts). false
:otp_app The name of the OTP app owning the priv directory where the can config file is stored
:priv_can_config_path The relative path where the Yaml config file is located
:enable_socketcand Wheter Cantastic should start the socketcand server on all configured interfaces. This allows to remotely access the CAN interfaces for debugging. false
:socketcand_ip_interface The IP interface on which socketcand should listen to. "eth0"

YAML configuration file

Cantastic requires you to define a YAML file describing the frames to be sent and received and how to interpret them. This allows you to declaratively define your CAN networks in a clear and maintainable format.

Example

---
can_networks:
  ovcs:
    bitrate: 500000
    emitted_frames:
      - name: contactors_status_request
        id: 0x100
        frequency: 20
        signals:
          - name: main_negative_contactor_enabled
            kind: enum
            value_start: 0
            value_length: 8
            mapping:
              0x00: false
              0x01: true
          - name: main_positive_contactor_enabled
            kind: enum
            value_start: 8
            value_length: 8
            mapping:
              0x00: false
              0x01: true
    received_frames:
      - name: car_controls_status
        id: 0x200
        frequency: 10
        signals:
          - name: raw_max_throttle
            kind: integer
            value_start: 0
            value_length: 16
          - name: raw_throttle
            kind: integer
            value_start: 16
            value_length: 16
          - name: requested_gear
            kind: enum
            value_start: 48
            value_length: 8
            mapping:
              0x00: drive
              0x01: neutral
              0x02: reverse
              0x03: parking

Detailed YAML file structure:

Top level property

Key Description Required Default value
:can_networks a map of can networks to connect to in the form network_name: {...network definitions...} True

Network properties

Key Description Required Default value
:bitrate The CAN network speed in bits per seconds. True
:emitted_frames An array of frame definitions. False []
:received_frames An array of frame definitions. False []
:obd2_requests An array of OBD2 request definitions. False []
Example
#  my_vehicle.yml
---
can_networks:
  my_network:
    bitrate: 500000
    emitted_frames:
      - name: frame1
      - .....
    received_frames:
      - name: frame2
        ....
    obd2_requests:
      - name: request1
      - ....

Frame definitions

Key Description Required Default value
:id The CAN Frame ID True
:name The CAN Frame name, will be used in your own code to reference it True
:frequency The frequency is milliseconds at which the frame should be emitted/is expected to be received True for emitted frames, False for received frames
:allowed_frequency_leeway The tolerance in milliseconds to be added to the frequency by the Cantastic.ReceivedFrameWatcher when monitoring the frame frequency False 10
:allowed_missing_frames The number of missed frames before Cantastic.ReceivedFrameWatcher should send handle_missing_frame messages to subscribers False 5
:allowed_missing_frames_period Timeframe in milliseconds during which Cantastic.ReceivedFrameWatcher is counting the number of missing frames False 5_000
:required_on_time_frames The number of frames received at the expected frequency to consider a frame back to 'normal' false 5
:signals An array of signals to be interpreted in this frame False []
Example
---
can_networks:
  my_network:
    bitrate: 500000
    received_frames:
      - name: frame1
        id: 0x100
        frequency: 20
        signals:
          - name: signal1
            ....
          - name: signal1
            ....

Signal definitions

Key Description Required Default value
:name The signal name, will be used in your own code to reference it True
:value_start The bit number where the raw signal starts True
:value_length The number of bits to use for this signal True
:kind The type of value to be returned, one of: "decimal", "integer", "static", "enum" False "decimal"
:precision The precision to which a decimal signal should be rounded to False 2
:sign Wheter the signal should be interpreted as a signed or unsigned integer False "unsigned"
:endianness The endianness to be used to interpret the signal False "little"
:mapping For "enum" values, a map for each integer value False {}
:unit An informational unit related to the signal's value False
:scale A decimal scale to be applied on the raw value, defined as a string in YAML False "1"
:offset A decimal offset to be applied on the raw value, defined as a string in YAML False "0"
:value For "static" values, the integer raw representation to be used False
Example
---
can_networks:
  my_network:
    bitrate: 500000
    received_frames:
      - name: frame1
        id: 0x100
        frequency: 20
        signals:
          - name: decimal_signal
            value_start: 0
            value_length: 8
            kind: decimal
            precision: 3
            sign: signed
            endianness: big
            scale: "0.3444"
            offset: "30"
          - name: boolean_signal
            value_start: 8
            value_length: 1
            kind: mapping
            mapping:
              0x00: false
              0x01: true
          - name: static_signal
            value_start: 9
            value_length: 8
            kind: static
            value: 0xAB

OBD2 request definitions

Key Description Required Default value
:name The OBD2 Request name, will be used in your own code to reference it True
:request_frame_id The CAN Frame ID to be used for the OBD2 request True
:response_frame_id The CAN Frame ID of the frame used for the response True
:frequency The frequency is milliseconds at which the request should be emitted True
:mode The OBD2 mode to be used True
:parameters An array of parameters to be interpreted in this request False []
Example
---
can_networks:
  my_network:
    bitrate: 500000
    obd2_requests:
      - name: obd2_request1
        request_frame_id: 0x7DF
        response_frame_id: 0x7E8
        frequency: 20
        mode: 0x01
        parameters:
          - name: parameter1
            ....

OBD2 parameters definitions

Key Description Required Default value
:name The parameter name, will be used in your own code to reference it True
:kind The type of value to be returned, one of: "decimal", "integer" False "decimal"
:precision The precision to which a decimal parameter should be rounded to False 2
:sign Wheter the parameter should be interpreted as a signed or unsigned integer False "unsigned"
:value_length The number of bits to use for this parameter True
:endianness The endianness to be used to interpret the parameter False "little"
:unit An informational unit related to the parameter's value False
:scale A decimal scale to be applied on the raw value, defined as a string in YAML False "1"
:offset A decimal offset to be applied on the raw value, defined as a string in YAML False "0"
Example
---
can_networks:
  my_network:
    bitrate: 500000
    obd2_requests:
      - name: obd2_request1
        request_frame_id: 0x7DF
        response_frame_id: 0x7E8
        frequency: 20
        mode: 0x01
        parameters:
          - name: speed
            id: 0x0D
            value_length: 8
          - name: rotation_per_minute
            id: 0x0C
            value_length: 16
            scale: "0.25"

Utilities

In order to keep your YAML file maintainable, Cantastic allows you to split it in multiple files and to import them using the following syntax:

import!:ovcs_mini/generic_controller/0x701_main_controller_alive.yml

Example:
#  my_vehicle.yml
---
can_networks:
  ovcs:
    bitrate: 500000
    emitted_frames:
      - import!:./frames/frame1.yml
      - ...
    received_frames:
      - import!:./frames/frame2.yml
      - ...
    obd2_requests:
      - import!:./obd2_requests/frame2.yml
      - ....
#  frames/frame1.yml
TODO

Real world example

Cantastic is used in the Open Vehicle Control System, you will find concrete usage example in this repository

More concretely: