Canadaper usage

The canadaper is a script distributed as part of the sgframework. It converts CAN signals to MQTT messages and MQTT messages to CAN signals. All MQTT communication is handled by the MQTT broker which controls the access. Most often only a specific subset of all available vehicle CAN signals is implemented in the canadapter. Typically the ‘DeployAirbag’ CAN signal is excluded from the implementation in the canadapter.

The configuration of the canadapter consists of providing it with information about the signals on the CAN bus, and a configuration file telling which of those that should be possible to send/receive over MQTT. The Canadapter uses the KCD fileformat for configuring the CAN signals. This file-format is an open-source alternative to the Vector DBC files. An DBC-to-KCD conversion tool is available online: CAN-Babel

The usecase for this prototype implementation is to extract a few signals from a CAN bus and possibly send a few CAN signals. Due to the use of the MQTT protocol (over TCP/IP) it will not fulfill any hard realtime requirements.

Configuration files for canadapter

There are two configuration files for the canadapter. One describes the frames and signals on the CAN bus, and is in the KCD file format. The other type of configuration file defines the names each of the CAN signals should have when sent as MQTT messages and is in JSON file format.

KCD file

The KCD file format is described here: https://github.com/julietkilo/kcd

Canadapter is using the KCD file format via the can4python library, which is found here: https://github.com/caran/can4python

Example of a KCD configuration file:

../examples/configfilesForCanadapter/climateservice_cansignals.kcd
<NetworkDefinition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://kayak.2codeornot2code.org/1.0"
                   xsi:noNamespaceSchemaLocation="Definition.xsd">
    <Document name="Vehicle simulator"></Document> 
    <Bus name="Mainbus">
        <Message id="0x007" name="climatecontrolsignals">
            <Signal name="acstatus" offset="7" length="1" endianess="big"/>
            <Producer>
                <NodeRef id="1"/>
            </Producer>
        </Message>
        <Message id="0x008" name="vehiclesimulationdata">
            <Signal name="vehiclespeed" offset="8" length="16" endianess="big">
                <Value type="unsigned" slope="0.01"/>
            </Signal>
            <Signal name="enginespeed" offset="26" length="14" endianess="big"/>
        </Message>
        <Message id="0x009" name="climatesimulationdata">
            <Signal name="indoortemperature" offset="8" length="11" endianess="big">
                <Value type="unsigned" slope="0.1" intercept="-50"/>
            </Signal>
        </Message>
    </Bus>
</NetworkDefinition>

Edit the KCD file to only contain the CAN frames and signals you are interested in, otherwise CPU capacity is used to parse CAN frames of data not is used.

JSON file

The JSON file defines the names of each of the CAN signals should be sent as MQTT messages. It also represents which of the CAN frames should be sent on MQTT and which MQTT messages should be allowed to be forwarded to the CAN bus.

Note however which messages are actually allowed to be sent to the CAN bus (they are also controlled by the CAN configuration file (KCD)) and which of the nodes on the bus are enacted by the canadapter (ego node id). The KCD file could hold information about all nodes and signals in the system.

For example, consider a KCD file defining that node 1 sends CAN frame id 6 and 7, and node 2 sends CAN frame 8 and 9. If the canadapter enacts node 2 (ego node id, set by a command line argument), then it is allowed to send CAN frame 8 and 9 according to the KCD file. The JSON file further defines that this instance of the canadapter only is allowed to send CAN frame 9.

It is possible to adjust the configuration so several CAN signals to be sent in a single MQTT message. Here is an example of such an MQTT wire message:

{"values":
   {
      "ADAS_Seg_MsgType": 1.0,
      "ADAS_Seg_Offset": 2.0,
      "ADAS_Seg_CycCnt": 3.0,
      "ADAS_Seg_EffSpdLmt": 4.0,
      "ADAS_Seg_EffSpdLmtType": 5.0
   }
}

If an MQTT message contains one CAN signal (extracted from a CAN frame), this is named a “signal” in the JSON configuration file.

If an MQTT message contains information about several CAN signals (all extracted from the same CAN frame)then it is named an “aggregate” in the JSON configuration file.

The top structure of the JSON configuration file is like this:

{"entities":
    {
        "signals": [],
        "aggregates": []
    }
}

Each “signal” object (in the list of signals) should have a structure like this:

{"canName": "indoortemperature",
 "canMultiplier": 1.0,
 "mqttName": "actualindoortemperature",
 "mqttType": "float",
 "mqttEcho": false,
 "toCan": false,
 "fromCan": true
}

Only the “canName” field of a signal is mandatory. The “mqttName” defaults to be the same as the “canName”. The “mqttType” is float by default, but could also be “int”. The field “mqttEcho” sets whether an incoming MQTT command should be echoed back as data on MQTT, and defaults to false. By default a signal is allowed to be converted from CAN (to MQTT), but not to CAN (from MQTT). The multiplier is used when converting a CAN signal to an MQTT signal. In the other direction is 1/multiplier used. Defaults to 1.0.

Example of a JSON configuration file containing only “signals”:

../examples/configfilesForCanadapter/climateservice_mqttsignals.json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
{"entities":
    {"signals":[
        {"canName":"vehiclespeed"},
        {"canName":"enginespeed"},
        {"canName":"indoortemperature",
         "canMultiplier": 1.0,
         "mqttName":"actualindoortemperature",
         "mqttType":"float",
         "mqttEcho":false,
         "toCan":false,
         "fromCan":true
        },
        {"canName":"acstatus",
         "mqttName":"aircondition",
         "mqttEcho":true,
         "toCan":true,
         "fromCan":false}
    ]}
}

Each “aggregate” object (in the list of aggregates) should have a structure like this:

{"mqttName": "ADAS_Seg",
 "toCan": true,
 "fromCan": false,
 "signals": [
      {"canName": "ADAS_Seg_MsgType", "mqttType": "int"},
      {"canName": "ADAS_Seg_Offset"},
      {"canName": "ADAS_Seg_CycCnt", "mqttType": "int"},
      {"canName": "ADAS_Seg_EffSpdLmt"},
      {"canName": "ADAS_Seg_EffSpdLmtType"}
   ]
}

For aggregates, only the MQTTname key is mandatory. The “signals” part of an aggregate are the same as defined above for an individual signal. Only the top level “toCan” and “fromCan” fields of an aggregate are used (not the fields inside the signals, if set). Defaults to allow conversion from CAN (but not to CAN).

Example of a JSON configuration file containing “signals” and “aggregates”:

../examples/configfilesForCanadapter/ADASIS_mqttsignals.json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{"entities":{
    "signals":[
            {"canName":"ADAS_Posn_MsgType", "mqttType":"int"},
            {"canName":"ADAS_Posn_Offset"},
            {"canName":"ADAS_ProfShort_CtrlPoint", "mqttType":"int"}],
    "aggregates":[
        {"mqttName":"ADAS_Posn_1",
         "signals":[
            {"canName":"ADAS_Posn_MsgType", "mqttType":"int"},
            {"canName":"ADAS_Posn_Offset", "mqttName":"Position_offset"},
            {"canName":"ADAS_Posn_CycCnt", "mqttType":"int"},
            {"canName":"ADAS_Posn_Spd"}]},
        {"mqttName":"ADAS_Posn_2",
         "signals":[
            {"canName":"ADAS_Posn_PosProbb"},
            {"canName":"ADAS_Posn_CurLane"}]},
        {"mqttName":"ADAS_Seg",
         "toCan":true,
         "fromCan":false,
         "signals":[
            {"canName":"ADAS_Seg_MsgType", "mqttType":"int"},
            {"canName":"ADAS_Seg_Offset"},
            {"canName":"ADAS_Seg_CycCnt", "mqttType":"int"},
            {"canName":"ADAS_Seg_EffSpdLmt"},
            {"canName":"ADAS_Seg_EffSpdLmtType"}]}
        ]
    }
}

Resulting MQTT messages

This is a part of the interpretation of the climateservice_cansignals.kcd file:

CAN frame definition. ID=8 (0x008, standard) 'vehiclesimulationdata', DLC=8, cycletime None ms, producers: [], no throttling, contains 2 signals
    Signal details:
    ---------------


    Signal 'vehiclespeed' Startbit 8, bits 16 (min DLC 2) big endian, unsigned, scalingfactor 0.01, unit:
         valoffset 0.0 (range 0 to 7e+02) min None, max None, default 0.0.

         Startbit normal bit numbering, least significant bit: 8
         Startbit normal bit numbering, most significant bit: 7
         Startbit backward bit numbering, least significant bit: 48

                  111111   22221111 33222222 33333333 44444444 55555544 66665555
         76543210 54321098 32109876 10987654 98765432 76543210 54321098 32109876
         Byte0    Byte1    Byte2    Byte3    Byte4    Byte5    Byte6    Byte7
         MXXXXXXX XXXXXXXL
         66665555 55555544 44444444 33333333 33222222 22221111 111111
         32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210


    Signal 'enginespeed' Startbit 26, bits 14 (min DLC 4) big endian, unsigned, scalingfactor 1, unit:
         valoffset 0.0 (range 0 to 2e+04) min None, max None, default 0.0.

         Startbit normal bit numbering, least significant bit: 26
         Startbit normal bit numbering, most significant bit: 23
         Startbit backward bit numbering, least significant bit: 34

                  111111   22221111 33222222 33333333 44444444 55555544 66665555
         76543210 54321098 32109876 10987654 98765432 76543210 54321098 32109876
         Byte0    Byte1    Byte2    Byte3    Byte4    Byte5    Byte6    Byte7
                           MXXXXXXX XXXXXL
         66665555 55555544 44444444 33333333 33222222 22221111 111111
         32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210

Run the Canadaper:

$ python3 scripts/canadapter.py examples/configfilesForCanadapter/climateservice_cansignals.kcd \
-mqttfile examples/configfilesForCanadapter/climateservice_mqttsignals.json -mqttname climateservice -i vcan0

Send a CAN frame with ID=8:

$ cansend vcan0 008#00FF00FF00000000

Study the traffic on the CAN bus:

$ candump vcan0

Resulting output:

vcan0  008   [8]  00 FF 00 FF 00 00 00 00

Study the resulting MQTT messages using this command:

$ mosquitto_sub -v -t +/#

Resulting MQTT messages:

data/climateservice/vehiclespeed 2.5500000000000003
data/climateservice/enginespeed 63.0

CAN adapter helptext

$ python3 scripts/canadapter.py -h
usage: canadapter.py [-h] [-mqttfile MQTTFILE] [-v] [-version] [-i INTERFACE]
                     [-host HOST] [-port PORT] [-qos {0,1,2}] [-k KEEPALIVE]
                     [-cert CERT] [-busname BUSNAME] [-mqttname MQTTNAME]
                     [-listentoallcan] [-bcm] [-t THROTTLINGTIME]
                     [-ego EGO [EGO ...]]
                     kcdfile

positional arguments:
  kcdfile             File name for CAN bus definition (in KCD file format).

optional arguments:
  -h, --help          show this help message and exit
  -mqttfile MQTTFILE  File name for mqtt-and-CAN signal name and permission
                      translation (JSON). Defaults to listen to all CAN
                      signals (no JSON file used).
  -v                  Increase verbosity level. Can be repeated.
  -version            show program's version number and exit
  -i INTERFACE        CAN interface name. Defaults to 'vcan0'.
  -host HOST          Broker host name. Defaults to 'localhost'.
  -port PORT          Broker port number. Defaults to 1883.
  -qos {0,1,2}        MQTT quality-of-service setting. Defaults to '0'.
  -k KEEPALIVE        Set keepalive time for MQTT communication to the broker,
                      in seconds. Defaults to 10 seconds.
  -cert CERT          Directory for certificate files. Defaults to not using
                      certificates.
  -busname BUSNAME    CAN Bus name in the KCD file. Defaults to use the first
                      busname (alphabetically).
  -mqttname MQTTNAME  Resource name for MQTT topics. Defaults to 'canadapter'.
  -listentoallcan     Listen to all CAN signals, and send them on MQTT using
                      the same signalname. Will be overrided by -mqttfile
                      option.
  -bcm                Use broadcast manager (BCM) for periodic sending of CAN
                      frames by the Linux kernel. Defaults to not using the
                      broadcast manager. See documentation for can4python.
                      Requires Python 3.4 or later.
  -t THROTTLINGTIME   Set throttling time (max update rate) for incoming
                      frames, in milliseconds. Is automatically setting the
                      '-bcm' option. Defaults to not throttle incoming frame
                      rate.
  -ego EGO [EGO ...]  Set ego node id (string), for defining which of the
                      frames in the KCD file should be sent. By default other
                      frames are received. Several ids can be given. Defaults
                      to '['1']'. See KCD file definition documentation.

A CAN adapter for the Secure Gateway concept architecture

This is a "Resource" according to the Secure Gateway nomenclature. It registers
on the Secure Gateway network, and accepts commands that will be sent over the
CAN network. Signals received from the CAN network are forwarded to
the Secure Gateway MQTT (over IP) network.

It is intended for running on a Linux machine having a CAN interface,
for example a Beaglebone or Raspberry Pi with appropriate expansion boards
(capes/HATs). It can also be used on simulated (virtual) CAN buses on Linux.

It requires a MQTT broker (for example Mosquitto), and a MQTT client library (Paho).

This resource can connect to the broker in a secure or insecure way.
The settings of the broker determines what is allowed. To connect in the secure way,
the directory of the certificate files must be specified.

The certificate files should be named:
  CA file:          ca_public_certificate.pem
  Certificate file: public_certificate.pem
  Key file:         private_key.pem