Developer Blog

22/03/2021 by Lars-Göran Fredriksson

How to connect to a specific Kvaser CAN channel

In this document I will show how to connect to a unique Kvaser CAN channel, using a nickname instead of CANlib channel number. This method makes it possible to dedicate a can channel to a specific interface.

1. Customized channel names

We (support@kvaser.com) often get the question:

  • Can I make sure that my computer always starts my kvaser interfaces in the same order?

The answer to that question is unfortunately: No!

Normally, if you have multiple interfaces, the interfaces will be started on the same order every time, but if you change one interface, then the order can be different.

  • Is it possible to name each channel with an identifier?

Yes, it is possible to set a name on each channel that can be used to identify a channel. 

(Will be shown in another blog later)

There is one quite simple method that does not require so much work, that makes it quite easy to open a unique CAN channel. All our (Kvaser) Interfaces do have a product number (EAN) and a serial-number, in combination with the “Channel Number On Card” these three data fields create a unique combination.

Example

pasted image 0 (4)

My “Kvaser USBcan Pro 2xHS v2” interface has two channels, no 0 and no 1, and it will be found at CANlib channel 1 and 2.

2. Finding channel information for a specific channel

unnamed (4)

If we check my  “Kvaser USBcan Pro 2xHS v2” interface, we will find:

Device EAN              73-30130-00752-9 (this is the product number)
Serial Number          12160
Channel on Card      0                           (On the first channel)
Channel on Card      1                           (On the second channel)

If I create a name based on this information, eg. “00752-9:12160/1”, then this name will always point to the second channel on my “USBcan Pro”, there is no other Kvaser interface that can have that combination.

So if I have connected  “00752-9:12160/1” to a very secret test object, then I know that if I open that name combination, I will always communicate with that object, and I do not need to know how many other interfaces I have installed in my computer. Something like canOpenChannel(‘00752-9:12160/1’,flags).

2.1. The CANlib command canOpenChannel()

Normally when we call the command canOpenChannel, then we provide a CANlib channel number, and some FLAGS.

Can we use the syntax canOpenChannel(‘00752-9:12160:1’,flags) instead?

Well, in the best of worlds, this command should already be a part of CANlib, but it does not exist yet, so you will have to do it yourself.

2.2. User defined MycanOpenChannel()

Let us create a command: MyCanOpenSpecificChannel()

Delphi
function MyCanOpenSpecificChannel(const NickName: string; const flags: integer; var FoundChannel: integer): canHandle;

Parameters

[in]    NickName           String with “EAN:SN/ChOnCard” (‘00752-9:12160/1’)
[in]    flags                   A combination of canOPEN_xxx flags
[out] FoundChannel     The number of the opened channel

Returns
Returns a handle to the opened circuit, or canERR_xxx (negative) if the call failed.

A complete listing of the functions MyCanOpenSpecificChannel() Appendix A.

Let us assume that NickName contains ‘00752-9:12160/1’. Let us call the function SplitNickNameStringPlease(), it will return EAN, SN and NoOnCard.

EAN             = ‘00752-9’
SN               = 12160
NoOnCard    = 1

With the command canGetChannelData() we can get a lot of information about our connected interfaces. We will use the parameters: canCHANNELDATA_CARD_SERIAL_NO, canCHANNELDATA_CHAN_NO_ON_CARD and canCHANNELDATA_CARD_UPC_NO.

canCHANNELDATA_CARD_SERIAL_NO uses a 64bit integer
canCHANNELDATA_CHAN_NO_ON_CARD uses a 32bit integer
canCHANNELDATA_CARD_UPC_NO uses a 64bit BCD string

So by calling(when iteration through all channels (I)):
canGetChannelData(I, canCHANNELDATA_CARD_SERIAL_NO , U64_1, sizeof(U64_1));
canGetChannelData(I, canCHANNELDATA_CHAN_NO_ON_CARD, U32_1, sizeof(U32_1));
canGetChannelData(I, canCHANNELDATA_CARD_UPC_NO , U64_2, sizeof(U64_2));

We will receive some information in U64_1,U32_1 and U64_2.
U64_1 and U32_1 will contain the Serial Sumber respective Channel No on Card, and can easily be compared with our search values.
U64_2 contains the EAN-number and we need to do some processing to be able to read the EAN number.
First, convert U64_2 to a 16 digit long HEX string,
2026405030294825 ToHexString becomes: ‘0007330130007529’

The full EAN number is 73-30130-00752-9 so it easy to identify what we must copy
73-30130-00752-9  ‘0007330130007529

st := copy(st, 11, 5) + ‘-‘ + copy(st, 16, 1);

We are looking for the condition:

EAN      = ‘00752-9’
SN       = 12160
NoOnCard = 1

If (U64_1=SN) and (U32_1=SN) and (EAN=st) then we have found our interface, let us return the channel number and try to call canOpenChannel().

You can see the complete listing of the function in Appendix A.

3. Conclusion

Of course, you can select almost any format to describe a certain unique channel.

What I tried to describe here in this text, is that with three calls to canGetChannelData(), you will receive enough information to be able to determine if this is the channel you want to open.

I hope this information is useful to you. Comments and questions are welcome!

4. Appendix A MycanOpenChannel()

4.1. function MycanOpenChannel()

function MycanOpenChannel(const NickName: string; const flags: integer; var FoundChannel: integer): canHandle;
    var
      I: integer;

      EAN       : string;
      SN        : UINT64;
      ChNoOnCard: UINT32;

      N: integer;
      UI64   : UINT64;
      UI32   : UINT32;
      st     : string;

    begin
      result       := canERR_NOTFOUND;
      FoundChannel := canERR_NOTFOUND;

      /// Most probably, NickName contains something like:'00752-9:12160:0'
      /// EAN=00752-9 (this is the last part of Device EAN	73-30130-00752-9)
      /// sn = 12160
      /// NoOnCard=0

      if SplitNickNameStringPlease(NickName, EAN, SN, ChNoOnCard) then
      begin
        // Let us see if we can find the combination of EAN, SN, ChNoOnCard

        if (canGetNumberOfChannels(N) = canOK) then
        begin
          for I := 0 to N - 1 do
          begin
            // Look for SN first
            if (canGetChannelData(I, canCHANNELDATA_CARD_SERIAL_NO, UI64, sizeof(UI64)) = canOK) then
            begin
              if (UI64 = SN) then
              begin
                if (canGetChannelData(I, canCHANNELDATA_CHAN_NO_ON_CARD, UI32, sizeof(UI32)) = canOK) then
                begin
                  if (UI32 = ChNoOnCard) then
                  begin
                    // We have found a channel with SN and NoC that is correct, let see if EAN is correct
                    if (canGetChannelData(I, canCHANNELDATA_CARD_UPC_NO, UI64, sizeof(UI64)) = canOK) then
                    begin
                      st := UI64.ToHexString(16);
                      st := copy(st, 11, 5) + '-' + copy(st, 16, 1);
                      if (st = EAN) then
                      begin
                        FoundChannel := I;
                        break; // Breaks the FOR-loop
                      end;
                    end;
                  end;
                end;
              end;
            end;
          end;

          if FoundChannel >= 0 then
          begin
            // Let us use "FoundChannel" and see what happens
            result := canOpenChannel(FoundChannel, flags);
          end;
        end;
      end;
    End;

4.2. function SplitNickNameStringPlease()

function SplitNickNameStringPlease(const NickName: string; var EAN: string; var SN: UINT64; var NoC: UINT32): boolean;
    var
      P   : integer;
      st  : string;
      temp: string;
    begin
      result := false;
      try
        // '00752-9:12160/0'
        temp := NickName;
        P    := pos(':', temp);
        if P > 0 then
        begin
          EAN  := copy(temp, 1, P - 1);
          temp := copy(temp, P + 1, 1024);

          EAN := trim(EAN); // remove leading and trailing spaces
          while length(EAN) < 7 do
          begin
            EAN := '0' + EAN; // add leading zeros if needed
          end;

          P := pos('/', temp);
          if P > 0 then
          Begin
            st := copy(temp, 1, P - 1);
            SN := st.ToInteger;

            st  := copy(temp, P + 1, 1024);
            NoC := st.ToInteger;

            result := true;
          end;
        end;
      except
        result := false;
      end;
    end;
Author Image

Lars-Göran Fredriksson

Lars-Göran Fredriksson is a Field Application Engineer for Kvaser AB. His background is in geographic information system (GIS) and Remote Sensing and his current focus is on connecting the deep knowledge of Kvaser's developers with the practical questions of our end users. If you doubt his passion for CAN, just know that his first week in the office he created an interactive CAN Trivia game that sent the office scouring the halls for the correct answers. He is a passionate fisherman who would like to develop new environmentally friendly fishing methods. Biggest catch and release fish is for the moment a Bluefin Tuna at appr 325kg / 715lbs.