CAN Part 6 - In Depth XNet

Okay so we've covered a lot of information in this series of posts so far.  From hardware to the different variants of CAN, and how it compares to other communication protocols.  We've talked about the raw data sent and received.  We've also talked about NI's offerings with NI-CAN and NI-XNet, along with several 3rd party APIs.  And we've also covered the conversion from raw frames to signals, and back.  In this post we're going to try to cover the more advanced features of the XNet API, and the different XNet sessions that can be used hardware.  XNet is different from all the other CAN APIs mentioned so far and so using it can sometimes be confusing at first.  But it is by far the most flexible and powerful of any mentioned in this series.

More than Just CAN

So the XNet API was designed to work with several communications types.  At the moment the API works with the CAN bus, the LIN bus, and Flexray.  The idea is that each of these APIs (and potentially others) all need an open, read, write, close, and database management functions.  Sure some things are going to be specific to one bus type, but by using this simplified API, anyone familiar with XNet can use it with other hardware types.  So while this blog series will focus on the CAN features of XNet, just know that many of the things talked about here apply to LIN, Flexray, and potentially others in the future.

Base XNet Palette

From looking at the XNet palette, NI has tried to make a very simple looking API, but hide away some of the more advanced features so that new users can get started quickly.  While still allowing for more low level control to those that need it.  First lets look at the base palette.

The Create Session, Read, and Write are all polymorphic functions that can adapt to different functions.  And the Session Node is a property node that can perform other useful functions on the session reference returned by the Create Session.  

Session Properties

Using the Session Node, you can read or write properties of the session, but you should be aware that writing to some properties, will effect all sessions on that hardware's port.  For instance one thing you can do with CAN is turn on and off a 120 Ohm resistor.  Back in Part 1 of this series we talked about how for a proper CAN network you should have two 120 Ohm resistors, one on each end of the bus, between CAN High and CAN Low.  The high speed XNet CAN hardware can turn on or off this resistor using software which is super handy for setups that may or may not need it.  This type of setting is clearly going to be turned on or off on the physical port.  And if you have multiple sessions using that single port, turning on or off the resistor will turn it on or off for all sessions using that port.  

The list of properties available are pretty extensive and some are network specific.  All are pretty well documented in the context help, and most have examples showing how their functions work.

XNet Session Types

There are 12 main session types for the CAN bus, and a conversion session.  Each of these sessions can be created using the Create Session polymorphic VI.  The conversion session was mentioned in Part 5 of this series, and is basically a way to convert from Signals to Frames and Frames to Signals but doesn't rely on any actual hardware.  There are 3 session types for Signal Input, 3 for Signal Output, 3 for Frame Input and 3 for Frame Output.  Luckily the read and write session types are similar and just work in reverse.  This leaves us with only 6 other session types that we will go over in more detail.  Each of these session types has at least one example installed with the API and can be found by going to Help >> Find Examples.

Frame In Stream

This session type works like all other APIs we've talked about so far as a CAN read.  If you want to read you call the read, and you'll get all the frames since the last read.  As the frames are seen by the hardware they will sit and wait in a hardware buffer until your software reads them from the buffer.  When you perform a read, all frames waiting in the hardware buffer get returned as an array.  This session type is useful when you want to view every frame coming in and either evaluate them or log them.  Here is a nice illustration made by NI.  

Notice that all frames came in earlier, and a single read returns them all at once.  The read function also has optional inputs which can specify things like reading a set number of frames, and having a timeout on waiting for those frames.

Frame In Single Point

The Single-Point session type is most useful on frames that you want to know the most recent value for, but don't really care about each individual frame.  When starting this session type you must specify an array of Frames that will be read and returned in the order requested.  These frames identifiers come from the database selected, which will be talked about later.  Since so many CAN devices will send out data periodically, it isn't always important to evaluate every frame.  Lets say a device sends out the device status every 10 milliseconds.  If our software is looking to react to when the status changes, then maybe a single point read is all we need to do since we will likely be reading at a much slower rate than the device is sending it.

The caveat here is that if the status changed twice between reads, we won't know it from the single point read, since that only returns the most recent value.  Additionally we may have to look at the timestamp data to ensure that we are still getting new values for the frame.  If the device sending the frame turns off, or stops sending it, any subsequent reads will return the newest values received for a frame.  For this session type you must specify all frames you expect to read at the start.  And if a frame hasn't been seen yet, the read will return the default value for the frame.

Here we see that the first read returns data for both IDs 0xC and 0xE even though 0xE hasn't been written to the bus yet.  Indicating this is the fact that the timestamp is zero.  The second read will again return the last value of 0xC and 0xE but in this case 0xE has changed twice but looking at this data we can't know this.

Frame In Queued

When creating a Frame In Queued session, a frame must be specified from the database to monitor, and can't just be done on the whole bus, like Frame In Stream, or on an array of frames like Frame In Single Point.  So if your software wants to perform a read, and return all frames from one specific ID then you can use the Queued session type.  This is most useful when a particular ID contains information that should be evaluated on every frame, but other IDs aren't as important.  One place this is useful is for when an incrementing counter is used.  Maybe a device will increment a number with every frame sent.  For this one ID we may want to read all frames to ensure the number does increment, but for all other IDs maybe the Single-Point read is more appropriate.  Since XNet allows us to open multiple sessions our application might work best with one Frame In Queued session, and one Frame In Single Point session.  Below is another NI illustration.  Note that in this case two sessions are opened, one for the 0xC and one for the 0xE IDs, with two separate reads, one for 0xC and one for 0xE

Signal In Single-Point

The Signal In Single-Point session is useful when you want to read a signal, but not a frame.  This read will not return the frame but instead will return the engineering units as a double, that have been scaled from the raw frame using the database as mentioned in Part 5.

This session type share many of the similarities and caveats as the Frame Single-Point mentioned earlier.  The Single-Point means a read will return the most recent value for each signal and multiple changes occurring between reads won't be detected, but unlike the Frame read the Signal read does not return a timestamp, so there is no way of knowing if the device is still transmitting new data or not using this session type alone.  All signals desired to be read must be specified on the Create Session.  And if a Signal hasn't been seen yet the default value for the signal will be returned when a read is performed.

Signal In XY

With Signal XY we read the engineering units of a signal, but this time we can read it for every value that the port sees.  This will also mean that if a signal hasn't been seen, it will return an empty array for that index.  Just like the other Signal session types, all signals intended to be read must be specified in the Create Session before performing the read.  Since frames come in at different rates, the times on each signal will correspond to when that signal's frame was read.  As a result all signals from the same frame will have the same number of samples, and same times for each sample.

Signal Waveform

This session type is like combining the Single-Point and the XY.  All samples for all signals specified will be returned.  But all signals read will have the same number of samples, the same start time, and the same delta X time between samples.  And the value for each signal at each time, will be as if a Single-Point reading was taken at that time.  Just like a Single-Point read there is no way to know if a signal is still being updated, or the signal value hasn't changed, unless the value changed between indices of the waveform.

The Other 6 Session Types

The last 6 Session types are similar to the first 6 except they are for writing signals and frames, instead of reading them.  For the most part they work the way you'd expect.  Single-Point sets a Signal or Frame, and will automatically send out the value periodically, until a new write is called at which point the value will be updated but still sent out regularly.  With the rate of transmission set by the database.  Signal Out XY can write a new value with each periodic write of the signal, with timing defined by the database.  Signal Out Waveform can update all signals with each change in time, but the periodic rate is still set by the database.  Frame Out Stream works like all other CAN APIs and will write all frames as fast as it can to the bus writing it once.  And the last one I want to spend a bit more time on later and that is the Frame Out Queued which makes a queue where each transmission sends a new frame, at the rate specified by the database.

Lots of Session Types I Know

There's lots of session types.  And at first this can seem a bit overwhelming.  But the good news is you can open multiple session types to each interface, so you can combine the strengths of one type, with the weaknesses of another.  Maybe you want Signal In Single-Point for reading the majority of the signals, but a Frame In Stream to log all raw frames, Frame Out Single-Point for most frames, and a Frame Out Queued for a couple special ones.  All of these can be open and running at once to make for some pretty flexible ways of sending data and receiving data.  The goal of all of this is to help offload the resources from the software you write, to the XNet hardware.  Sure a single Frame In, and Frame Out session can do all you need if you want to perform software timing on re-transmits, and software scaling from frames to signals, but a more efficient way is to have the hardware deal with timing and conversions.


So I've mentioned a few times how databases help with the timing of transmitted data.  In Part 5 we talked about the frame and signal conversion, and how signals are data embedded in the 8 byte payload of a CAN frame.  With a database in place, we can define how the conversion takes place.  This is somewhat like how DAQmx has virtual channels which can be saved which define how to read and scale raw voltage into a signal with engineering units.  If a DBC file is provided, the simplest way to use it is to import it into the XNet Database editor.  If one isn't provided, then this database can create be created from scratch using the XNet Database editor which is pretty useful at visualizing the frames and how the data gets converted.  But what if I don't have a DBC, and the database needs to be created based on some other document specifications like a custom spreadsheet?  How do I get and use this scaling information?  Well the XNet API can do all those things for you, but it has a bit of a learning curve.

Creating a database manually can be done using the XNet Database Editor pretty easily.  Doing it programatically is not as easy but luckily there are several examples included.  Open up the Editing Database with IO example from the Help >> Find Examples.

Database Levels

The database consists of several levels.  The first level is the file or database.  A database doesn't need to be stored as a file and can be just a database in memory but all changes are lost if that database isn't saved.

Within the database you have between 0 and N clusters.  A cluster is for a particular protocol, but you can have multiple clusters with the same protocol.  This can be useful if an application has multiple CAN buses that normally use different DBC files.  Each file can be associated with a cluster and the result is a single database file that defines the application.

Within the cluster there is the frame, which we covered in Part 2 of this series.  And within a normal frame we can have between 0 and 64 signals which were covered in Part 5 of this series.  Using the example mentioned earlier we have the ability to make Databases, Clusters, Frames, and Signals, as well as remove Signals, Frames, and Clusters from an existing database.

Through looking at the code that edits the Database you'll see the functions needed are a few polymorphic VIs like Create Object and Delete Object, and you'll see lots of property nodes.  These are all found on the Database palette of XNet.

When dealing with remote targets like an embedded cRIO or cDAQ, some functionality is missing from the normal Windows experience.  Things like importing and exporting a database aren't supported.  But instead what can be done is a database can be imported on the host OS, then deployed using the IP address of the remote target.  The rest of the editing of databases on the RT target seem to work as expected.  If you want to be able to perform an import of a DBC from an RT target feel free to kudo my idea on the Idea Exchange board.

If you've made it this far into this blog series I commend you.  XNet itself is a very large topic, and one that a multi-week class could be taught on.  But moving forward we will be done talking about all the various NI and non-NI APIs for talking to hardware.

Part 7...

In the previous parts of this blog we talked about hardware options, software options for NI and non-NI hardware, reading and writing CAN Frames, as well as database management and scaling with CAN Signals.  In Part 7 we will be talking about how to read and write CAN data to various file standards.  As well as look at some other CAN File I/O functions.