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.
Developer Blog
How to connect to a specific Kvaser CAN channel
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
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
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;