Developer Blog

21/05/2024 by Adam Raymer

A simple script that turns a programmable Kvaser device into a gateway

Situations may arise where you need to combine two CAN buses or transfer data between two buses that use different bit rates and/or protocols. You could purchase a gateway to accomplish this task. Or, you can use your own multi-channel Kvaser device and t script to create your own gateway. In this article, we will give a brief introduction to t script and how little code needs to be written to set up your own gateway. 

What is t script? 
Our t programming language is a C-like language that allows users to programmatically react and respond to certain events on the bus using “hooks.” The t script runs locally on the Kvaser unit. Once the t script is loaded and launched, there is no need for additional communication with a PC.*

How would you set up a Gateway? 
First, you would need to have a multi-channel device that can use script, such as a Kvaser USBcan Pro (2xHS or 4xHS) or a Kvaser Memorator Pro (2xHS v2 or 5xHS). Then connect Channel 1 to one CAN bus, and Channel 2 to another CAN bus. If you have a device that has more than two channels, you can plug in different channels as well. For our examples, we will use Channels 1 and 2. If you are using a Memorator Pro in stand-alone mode, you will need to provide power to Channel 1. 

Using TRX, Kvaser’s Integrated Development Environment for creating, compiling and running scripts, you would use the following script and download it to the device on Channel 1: 

variables {
  const int ch1 = 0;
  const int ch2 = 1;
}

on start {
  canSetBitrate(ch1, canBITRATE_250K);
  canSetBitrate(ch2, canBITRATE_250K);
  canSetBusOutputControl(ch1, canDRIVER_NORMAL);
  canSetBusOutputControl(ch2, canDRIVER_NORMAL);
  canBusOn(ch1);
  canBusOn(ch2);
}

on stop {
  canBusOff(ch1);
  canBusOff(ch2);
}

on CanMessage <ch1> * {
  canWrite(ch2, this);
}

on CanMessage <ch2> * {
  canWrite(ch1, this);
}

When you run this script in TRX, Channels 1 and 2 will both go on bus with a bit rate of 250 kb/s. After that, traffic from the first CAN bus will be transferred to the second CAN bus and vice versa. 

What is happening?
This script is broken into four “hooks”: on start, on stop, on CANMessage <ch 1> and on CANMessage <ch 2>. When the script is loaded and told by TRX to begin, “on start” is triggered. In this code, the channels will be placed bus on. When you tell TRX to stop the script, “on stop” is triggered and the channels will be placed bus off. If a message is read by CAN Channel 1, on CANMessage <ch 1> is triggered to run the code to copy the message to CAN Channel 2. If CAN Channel 2 reads a message, then the code in on CANMessage <ch 2> is run.**  

In these basic steps, you can have a functional gateway to connect two CAN buses. But what if you want to do more? 

What if the device is already on the bus? 
Because the device is already on bus, you don’t need the on start and on stop hooks. They can be removed if desired for readability: 

variables {
  const int ch1 = 0;
  const int ch2 = 1;
}

on CanMessage <ch1> * {
  canWrite(ch2, this);
}

on CanMessage <ch2> * {
  canWrite(ch1, this);
}

If you are using a device like a Kvaser USBcan Pro 2xHS v2, Kvaser USBcan Pro 4xHS, or Kvaser Hybrid Pro 2xHS, these are the steps you would take to start the script: 

  1. Go to your Bus Monitoring/Bus Analysis software  
  2. Configure the device and open channels  
  3. Launch TRX and load your project  
  4. Set the device in TRX to “Online” 
  5. In TRX, download the t script to the device 
  6. In TRX, start the t script 

Now your device is functioning as a gateway, and you can still use your Bus Analysis / Bus Monitoring Software. This completes our example of how to set up a basic gateway. But what if you want to connect two buses of different bit rates? Well, you can set the CAN Channels to the desired bit rates: 

on start {
  canSetBitrate(ch1, canBITRATE_250K);
  canSetBitrate(ch2, canBITRATE_500K);
  canSetBusOutputControl(ch1, canDRIVER_NORMAL);
  canSetBusOutputControl(ch2, canDRIVER_NORMAL);
  canBusOn(ch1);
  canBusOn(ch2);
}

If you are using software to monitor the bus, then make the configurations there. If you need more than two channels, you can add more on CanMessage hooks for those channels. 

Transfer certain Message IDs to the other channel: 
We can easily filter by ID using the following hook. This hook event will only send a message with an 11 bit ID 1019 (in decimal) to go to CAN Channel 2. 

on CanMessage <ch1> 1019 {
  canWrite(ch2, this);
}

Going from CAN to CAN FD: 
Going from CAN to CAN FD (or vice versa) is more complex. You cannot copy the message directly. The message needs to be reformatted in a CAN FD format.

For this example, we set CAN channel 2 to CAN FD with a nominal bitrate of 500 kb/s and a data phase bitrate of 2 Mb/s. Any message received by CAN Channel 1 would be transferred to a CAN FD format for CAN Channel 2, and any CAN FD message from CAN channel 2 is reformatted to CAN to be sent on channel 1. This example uses a maximum DLC of 8 for messages transferred from both CAN and CAN FD. In a more advanced example, you would need to also consider other differences between CAN and CAN FD when transferring the message like CAN FD frames larger than 8 data bytes.

variables{
  const int ch1 = 0;
  const int ch2 = 1;
  CanMessageFd msgFD;
  CanMessage msg;
}

on start {
  canSetBitrate(ch1, canBITRATE_250K);

  canSetCommunicationMode(ch2, canMODE_CAN_FD);
  canSetBitrate(ch2, canFD_BITRATE_500K_80P);
  canSetBitrateFd(ch2, canFD_BITRATE_2M_80P);

  canBusOn(ch1);
  canBusOn(ch2);
}

on CanMessage <ch1> * {
  if ((this.flags & (canMSG_RTR | canMSG_ERROR_FRAME)) == 0) {
     msgFD.id = this.id;
     
     msgFD.flags = canFDMSG_FDF | canFDMSG_BRS;
     if ((this.flags & canMSG_EXT) != 0) {
       msgFD.flags = msgFD.flags | canMSG_EXT;
     }

     if (this.dlc > 8) {
        msgFD.dlc = 8;
     } else {
        msgFD.dlc = this.dlc;
     }
     msgFD.data  = this.data;

     canWrite(ch2, msgFD);
   }

}

on CanMessageFd <ch2> * {
  if ((this.flags & canMSG_ERROR_FRAME) == 0) {
     msg.id = this.id;
     msg.flags = 0;
     if ((this.flags & canMSG_EXT) != 0) {
        msg.flags = canMSG_EXT;
     }

     if (this.dlc > 8) {
        msg.dlc = 8;
     } else {
        msg.dlc = this.dlc;
     }
     msg.data  = this.data;

     canWrite(ch1, msg);
   }
 }

on stop{
  canBusOff(ch1);
  canBusOff(ch2);
}

How can all of this be useful?
Instead of having to buy a whole new device to use as a gateway, you can use an existing compatible Kvaser device to act as one. Kvaser’s t programming language allows for nimble solutions to common problems using the hardware you already have. This can save you time and resources. 

This is just one example of the kinds of problems that can be solved with a t-enabled device and a few lines of code. For more examples, please take a look at some of our other DevBlogs. 

*That said, if your device is being used as a USB interface, do be aware that unplugging your device from the PC while the script is running will cause the t script to stop.  

**Please note that there is a 1 ms delay as the information is processed because the t script scheduler can only handle a single non-timer hook event per pass. In certain cases where multiple messages are sent at the same time, a backup can occur because the scheduler will handle each successive message on the next pass and the next in 1 ms intervals. Please take this into consideration when integrating this example into your network. 

Author Image

Adam Raymer

Adam Raymer is a Field Applications Engineer for Kvaser AB, based near Detroit, USA. Adam regularly meets with customers and is an active member of our global support team.