Send and receive database signals

  • February 15, 2018
  • Magnus Carlsson

This is the second post in a 2-part series about how to manage DBC databases using the Kvaser database library (kvadblib) and Python:

  1. Handling CAN databases in Python
  2. Send and receive database signals

 

In the first post we created a DBC database and took a peek inside to verify that we got what we asked for. Now we will use the database to send signals with physical values set from Python.


Send and receive CAN messages

Before sending signals using our database, let us first remind ourselves on how to write and receive a raw CAN message using Python.

from canlib import kvadblib
from canlib import canlib
from canlib import Frame

def open_channel(channel):
    ”””Open a new channel, set bitrate 1Mbit/s and go bus on.”””
    ch = canlib.openChannel(channel, canlib.canOPEN_ACCEPT_VIRTUAL)
    ch.setBusOutputControl(canlib.canDRIVER_NORMAL)
    ch.setBusParams(canlib.canBITRATE_1M)
    ch.busOn()
    return ch


def close_channel(ch):
    ”””Go bus off and close channel.”””
    ch.busOff()
    ch.close()

def send_receive_raw(ch0, ch1):
    # send a CAN frame without using kvadblib
    frame = Frame(id_=4,data=bytearray(b ’\x00\x00\x00\x00\xd8\x00\x1e\x00 ’))
    print(”Sending frame : %s” % frame)
    ch0.write(frame)

    # read message back
    print(”Receiving frame: %s” % str(ch1.read()))


ch0 = open_channel(0)
ch1 = open_channel(1)

send_receive_raw(ch0 , ch1)

close_channel(ch0)
close_channel(ch1)

Listing 1: Code sample showing how to write and receive a CAN message.

There should be no big surprises here1, sending the CAN message on channel zero is done with ch0.write(), receiving is done on channel one using ch1.read(). Running this small sample shows the following on standard out:

Sending frame: Frame(id=4, data=bytearray(b’\x00\x00\x00\x00\xd8\x00\x1e\x00’), dlc=8, flags=0, timestamp=None)
Receiving frame: Frame(id=4, data=bytearray(b’\x00\x00\x00\x00\xd8\x00\x1e\x00’), dlc=8, flags=2, timestamp=4)

Binding CAN messages

The first step when sending CAN signals is to open a database and associate a CAN frame with a message that is defined in the database.

# open the database we would like to use
db = kvadblib.Dbc(filename=’db_histogram.dbc’)

# fetch the message we are going to use from the database
lim_002 = db.get_message_by_name(’LIM_002’)

# create a BoundMessage, containing an empty frame with the correct
# id and dlc.
bmsg_0 = lim_002.bind()

When we bind a database message, we get a new object with space allocated for ID, DLC and Data, and at the same time the ID and DLC has been set according to the message specification. Now we can use the newly created BoundSignal to access our signals as attributes.

# We set the raw value of our signal ’Load ’ using attributes
bmsg_0.Load.raw = 0xff

print(”The bound message now looks like:\n%s” % bmsg_0)

print(”Load signal as raw value: %s” % bmsg_0.Load.raw)
print(”and as physical value: %s” % bmsg_0.Load.phys)

# More often we are interested in setting the physical value of the signal
bmsg_0.Load.phys = 75

print(”Load signal as raw value: 0x%x” % bmsg_0.Load.raw)
print(”and as physical value: %s” % bmsg_0.Load.phys)

Running the above code results in the following on standard output:

The bound message now looks like:
Frame: message_name:LIM_002, data:bytearray(b’\xff\x00\x00\x00\x00\x00\x00\x00’)
Load signal as raw value: 255
and as physical value: 3.5733110840282835e-43
Load signal as raw value: 0x42960000
and as physical value: 75.0

Send and receive CAN frames

Now that we have our bound message with the correct content, we use ._frame to extract the frame in a format suitable for the call to write().

# send a CAN frame using standard write () method
ch0.write(bmsg_0._frame)

# create another BoundSignal, this time containing a frame read from
# channel 1
bmsg_1 = lim_002.bind(ch1.read(timeout=100))

Taking the this new code and combining it with our first code sample, we end up with the following final result:

from canlib import canlib
from canlib import kvadblib

def open_channel(channel):
    ”””Open a new channel, set bitrate 1Mbit/s and go bus on. ”””
    ch = canlib.openChannel(channel, canlib.canOPEN_ACCEPT_VIRTUAL)
    ch.setBusOutputControl( canlib.canDRIVER_NORMAL)
    ch.setBusParams( canlib.canBITRATE_1M)
    ch.busOn()
    return ch

def close_channel(ch):
    ”””Go bus off and close channel.”””
    ch.busOff()
    ch.close()

def send_receive_sig(ch0, ch1):
    # open the database we would like to use
    db = kvadblib.Dbc(filename=’db_histogram.dbc’)

    # fetch the message we are going to use from the database
    lim_002 = db.get_message_by_name(’LIM_002’)

    # create a BoundMessage, containing an empty frame with the correct
    # id and dlc.
    bmsg_0 = lim_002.bind()

    # Set signal value
    bmsg_0.Load.phys = 75

    print(’Sending: %s’ % bmsg_0.Load)
    # send a CAN frame using standard write() method
    ch0.write(bmsg_0._frame)

    # create another BoundSignal, this time containing a frame read from
    # channel 1
    bmsg_1 = lim_002.bind(ch1.read(timeout=100))

    print(’Received signal Load: %s %s’ % (bmsg_1.Load.phys, bmsg_1.Load.unit))

ch0 = open_channel(0)
ch1 = open_channel(1)

send_receive_sig(ch0, ch1)

close_channel(ch0)
close_channel(ch1)

Running our last code sample gives the following result.

Sending: <BoundSignal: name=’Load’, phys=75.0>, unit:metric ton
Received signal Load: 75.0 metric ton

We have now scratched the surface on how to take advantage of the Kvaser database library (kvadblib) in order to use signals instead of creating and sending CAN messages manually.


Footnotes

1 The usage of canlib.Frame is new in v1.5 so that could have been a small surprise… 😉

Author Image

Magnus Carlsson

Magnus Carlsson is a Software Developer for Kvaser AB and has developed firmware and software for Kv...