“I despise the pleasure of pleasing people that I despise.” - Lady Mary Wortley Montagu

Pic 3

Hooovahh's Blog

CAN Part 11 - XNet Signal Drop Out and Triggering

07/03/2018 02:44 AM

The most common XNet session type I've seen used is probably Signal Single Point.  In some testing environments you may want to know the time between every frame, but in most cases knowing what the newest value is, is acceptable.  For instance you might want to know the temperature of an ECU by reading a CAN signal sent by the ECU.  Do you need to get the message telling you the ECU is at 25 degrees C, 100 times a second?  Probably not.  And does it matter if one frame comes in a few milliseconds after it was expected to?  Also probably not.  We just expect this signal to change very slowly, and maybe we react if it goes above or below a threshold.  Single Point is great for these kinds of signals.  Just get me the last value for the signal, and I'll react to it, or log it as needed, knowing I might be missing some values between reads.

Signal Single Point Flaw

So since the most common type of session I've run into is Single Point, I've looked at a few ways to make this session type even better.  One flaw of the single point session read type, is that you can't know how long ago the value came in.  When you perform a read on the frame type it returns a timestamp in the cluster that is the frame.  But with the Signal type all you get is a double that corresponds to the engineering units of that signal.  So we might perform an XNet read and be told the ECU is at 25 degrees C, but what we can't know from that read alone, is how long ago the ECU told us that.  In an extreme situation, if after the first successful read the DB9 becomes disconnected from the XNet hardware, then every read performed after would still return 25 degrees C.  Dispite the fact that there has been no CAN traffic for some time.  This is because the Single Point read has no timeout.  It just holds the last read value until a new value replaces it.  If a new value never comes, then the old value is always returned.

This type of Signal Single Input flaw can make for buggy looking user interface if all you do is perform a read and display it.  A user might see values when the ECU is off, or disconnected for instance.  Contrast this with CANalyzer which will show the engineering units of the last read value, but next to the signal is a rotating ball that stops if CAN traffic stops.  This is a visual indicator that the last read value was there at one point, but that new data isn't coming in anymore.  There are a couple of techniques to be able to detect this signal drop out with XNet, but the one that I find the most efficient is to utilize the trigger feature of the XNet device.


When creating most XNet sessions, a Signal List, or Frame List must be provided.  This is an array of the XNet Frame, or XNet Signal data types.  This data type is basically a string with some extra useful features.  All string manipulation functions operate on these data types as if they were strings, and the XNet Create Session subVIs also accept an array of strings.  I mention this because this allows for programmatically creating the list of signals or frames to read or write.  This also has the option for making strings that normally don't show up in the drop downs when you click the constant or control.  One such signal or frame type is the Trigger type.  This is a Frame or Signal reading function that will return a 0 or a 1, if a particular Signal or Frame has been updated since the last read.  The format for this string is the following:

:trigger:.<Frame Name>.<Signal Name>


:trigger:.<Frame Name>

Here is a quick example of how to create a trigger Signal, for every actual Signal intended to be read:

What we see here is a list of 3 signals to read on the left.  This goes through a For Loop, and generates the 3 corresponding trigger signals and creates a new session for these 6 Signals, with the trigger signals coming after the value signals.  A read will now return the value of the Signal, followed by the Signals Trigger Signal.

Holding Values

Looking at the rest of the example VI from above you'll notice that the rest of this VI now allows for the Signal In Single Point session type to have a hold function.  Where it will hold the value of a set of given signals for some amount of time; in this case 2 seconds.  At which point the signal's value will be set to NaN (not a number).  It does this by reading the trigger and if it is 1, then we know that signal has a new value.  If it is 0 then we should keep monitoring this signal, and if after two seconds the trigger still doesn't come then we can react.  Another technique I've tried to get this same result, would be to use the Signal XY session type, then read all the values, and throw away all of them except for the latest one.  If there are no new values to read, then we know that signal hasn't been sent, and to set the value to NaN if it continues for 2 seconds.  This also works but I found there was a decent amount more CPU and memory usage when trying to read all values and all times for a large list of signals, only to throw away all the old values.  This triggering technique works well and seems to be the most efficient way to get what I want.

Using this technique we can now show the last read values of a signal, but if no new data has come in then the values will be set to NaN to indicate this loss of communication.  Ideally we would do something like CANalyzer and still show the last value, but have a new indication that the data might not be accurate since the signal is gone.  But I've found most of the time it doesn't really matter what the last reading was, it is just important to show that it is gone.  Still someone could write a wrapper around the Signal Single Point read function where it returns an array of values, and an array of booleans indicating of the values have reached their timeout and are no longer valid.

CAN Part 10 - Running Code on XNet Hardware (continued CRC and Counter)

01/21/2018 10:37 PM

NI-CAN hardware was useful enough when it first came out.  You could read and write CAN frames, and with some hardware read and write CAN signals, and that was about all that was asked for from developers.  But as time went on NI saw the need to come up with a more flexible set of API drivers, and I'm guessing they thought this would be a good opportunity for a hardware refresh too.  This is where XNet came in and while I've already covered several topics on XNet, I know of one new topic that hasn't been covered anywhere because it is an undocumented, and incomplete feature going back to the the LabVIEW 8.6 era.  And I'm going to peel back just a bit of the curtain here in the hopes that others will find it useful and possibly so NI will put efforts into making it an official feature.

...So What Is It?

Okay so let me drop what seemed like a bomb shell to me, when I first heard about it.  All XNet hardware (as far as I know) has the ability to run arbitrary code that can be loaded at run-time from within LabVIEW.  What this means is XNet hardware has the ability to function as it always does, reading and writing data and working within the confines of the XNet API.  But in addition to that, it has the ability to load compiled binaries, and run code on specific events.


Well I don't want to go into all of the details but lets just say there are lots of restrictions.  The code that can be ran is not LabVIEW, it is texted based code, compiled to the specific microcontroller on the XNet hardware.  In addition to that, the events that code are ran on are pretty limited.  The code that you write does not run periodically and has pretty limited access to global information.  

I was hoping an entire protocol like ISO 15765, or XCP, could be loaded on the hardware so that the host software wouldn't need to handle that, but that isn't currently possible.  I also thought it might be useful to have the hardware always reply to a specific frame with a specific frame.  But again the limitations at the moment mean this isn't possible.

...So What Good Is It?

At the moment, the only real thing I see that is useful with this functionality, is to perform an action, just before a frame is to be sent out.  This means just before a frame is to be sent out, an action can be done.  The most common thing I can think of is to modify the payload of the frame that is to be sent out, but I think other things could be done like preventing a frame from going out.  This event is triggered when the hardware is preparing to send the frame, and not necessarily when the XNet Write function is called.  This means a periodic frame that is sent out every 10ms, will call the custom code every 10ms, after the first XNet Write call.

Great How Does It Work?

Okay so remember when I said this was undocumented?  Well this is where I start holding back on what I know.  NI unofficially said I could post this information publicly, if I didn't go into the whole process of how to setup the tool chain, write code, compile code, deploy it, and open up the properties that allow this to work.  The reason for this is because this functionality is relatively old, and not many in NI know it exists, let alone how to support it.  If a flood of people start calling NI for support on tool chain issues, and debugging deploying code to hardware that shouldn't be deployed to, then NI will regret ever allowing me to talk about this publicly.  

Uh...So What Can You Tell Us?

Here is the good news.  Even though I can't show how to setup code, and compile it, I can share already compiled code, and demonstrate how it works for a couple of common uses.

CRCs and Counters Again?

Yes...again.  So back in Part 9 we talk about CRCs and Counter signals inside a payload of a frame.  Here we talk about how some devices require a counter to change value with every frame being sent, and we discuss the bucket filling problem, which ensures the values are correct, but rely on the hardware to send them at the specified rate.  Well this is the perfect application to our custom XNet code.  Take a look at this example, which is part of the CAN XNet Tools package, found in the CAN Drivers download:

So here we see that we setup the frame out single point session like normal.  Except we have an initialize and configure function that I wrote which is called before starting the session.  These two VIs are password protected (by NI's request) but I can describe what is in them, since it isn't very helpful.  The configure function takes a premade binary that NI has generated, which tells the XNet how to perform incrementing counters, and CRCs.  This binary is downloaded to the XNet using a hidden property node, and it then sends some config information.  The second subVI just sends down more config information using hidden property nodes.  The secret sauce in all of this is really the binary which can't be modified without the whole tool chain and source.

So after running these two functions, and starting the interface, a frame will be sent out every 10ms with a new value for the counter, and a new value for the CRC.  In the demo the counter is the first byte of the payload, and the CRC is the last byte in the payload.  The write function just updates the values of the other 6 bytes in the payload.  You can download the source and run it yourself.  I didn't make a package for this and am just uploading a zip with the VIs in it that are needed. Download can be found here.  The only dependency is on LabVIEW 2015 or newer, and you need the XNet drivers installed.

Part 11...

Well who knows when it will be but hopefully my next post will be about CCP and XCP.  I don't have a library written for performing that protocols functions, but I'd find it pretty useful and also might then lead into how to flash ECUs using the CCP/XCP or ISO15765 protocols.  I just don't really have time, or a need for CCP/XCP at the moment.  Still check back in once in a while and feel free to send me comments and suggestions on the NI forums. 

CAN Part 9 - CRCs and Incrementing Counters (Bucket Filling Technique in XNet)

01/21/2018 10:36 PM

As this blog series gets longer these intros become less meaningful.  The intent of the into is to summarize all the information covered in the previous blog posts, but by now we've basically crammed a year or so of practical CAN knowledge into 8 blog posts.  As a result trying to summarize it two sentences really practical.  Just know lots of CAN stuff has been talked about, and this post is going to build on top of the previous knowledge of reading and writing raw CAN frames, as well as the in depth XNet discussed in Part 6.  In this blog post we are going to cover the incrementing counter and CRC that sometimes takes place in automotive communications, as well as ways of satisfying these requirements using hardware timing on XNet devices.

Incrementing Counters

In automotive based CAN, it is not uncommon to have a rolling counter on some signals in a CAN Frame.  The purpose is to ensure the integrity of data being sent, by incrementing a value with every transmission of the frame.  Then any device on the bus can read all frames being transmitted, and can determine if the device is still talking, and still performing the task of incrementing the counter, which may give some insight into the status of the device.  If a listener on the CAN bus still sees frames coming in, but they are not incrementing, then I can know that some part of the device is still functioning and I might be able to better understand the devices status.  Here is an example of an incrementing counter:

Time 0.000 ID 0x10 Payload: 0x00 00 00 00 00 00 00 00
Time 0.010 ID 0x10 Payload: 0x00 00 00 00 00 00 00 01
Time 0.020 ID 0x10 Payload: 0x00 00 00 00 00 00 00 02
Time 0.030 ID 0x10 Payload: 0x00 00 00 00 FF 00 00 03
Time 0.040 ID 0x10 Payload: 0x00 00 00 00 FF AB 00 00
Time 0.050 ID 0x10 Payload: 0x00 00 00 00 00 AB 00 01
Time 0.060 ID 0x10 Payload: 0x00 00 00 00 FF AB 00 02

Here we see that the last byte in the payload counts from 00 to 03, and then resets back to 00.  The other bytes of the payload may change or they may not, but that last byte must be incrementing every time it is sent, and in our example that is every 10ms.

Being a listener on a CAN bus, and determining if another device is still talking isn't too difficult to do in any API.  For most APIs you can just read all frames, pull out the counter value and compare it to the previous counter value that was read.  For XNet things can be a bit easier since we can perform a buffered read on Signals or on Frames getting all values that have been sitting in the buffer.

But if we are to simulate a device on the bus, and are required to increments a value, then the timing of our software is going to be much more critical.  Most devices looking for an incrementing counter have a relatively short timeout and may go into a faulty state if our software doesn't write the incrementing values in a timely manor.  It is not uncommon to have timing requirements of calculating the new CAN frame value, and sending a new one out every 10ms.  In Windows the reliability of timing is always a struggles.  In any general purpose operating system many other application might be trying to get the CPUs to perform their work.  Having a 10ms timing requirement is possible in software timing, but will likely have lots of jitter sometimes maybe taking 20ms, or more on overloaded systems.

So what's the solution?  Well some CAN hardware have the ability to automatically retransmit CAN frames on the hardware level, which doesn't require software to continually perform functions to keep the transmission going.  When we explored the XNet API in Part 6 we saw that some of the modes like Single Point allowed us to perform the write once, and then the database would handle how often the data would actually be written.

If we use a write mode like Single Point then we can be sure the data will go out every 10ms since it is hardware timed, but what we be able to do is change the value with every frame going out.  For this level of control what we are going to need is Queued mode.

Concept of Filling a Bucket

If we want to use XNet to allow for control over every frame going out, and have the timing be hardware controlled we are going to need something like this example illustration:

At the start of the application start we want to write several frames into a buffer which will be sent one at a time by the hardware.  The amount of frames put into this buffer doesn't matter too much, we just want to ensure that the buffer doesn't reach zero elements before we are able to put more in.

At 10ms the first frame goes out, then 20ms the next then 30ms the next.  This timing of transmission is handled by the hardware, but this only works if there are still more items in the buffer to send.  If there are no more frames in the write queue then the transmission will stop.  This means as the bucket is being emptied we need to make sure we fill it back up with another write function.

Now we see that at 40ms the next frame goes out, but what our software needs to do is ensure the bucket (or queue) never gets emptied completely, and adds some more frames to be sent out.  We need to keep track internally of the last value we wrote back at Time = 0 and start writing more adding one to the last written.  In our example we last wrote 01, so the next write needs to start at 02, so we write 02, 03, 00, and 01.  This is what I call the bucket filling technique, where the frames are being written one at a time, but we add new frames to be written every so often.  The timing of our write call isn't critical at all and in our example we first added 6 frames, so we need to write more within 60ms since that is how long it will take for the queue to be emptied.

Now that we have the concept lets look at how this appears in LabVIEW

Basic XNet Bucket Example

Here is a basic example which closely follows the images from earlier.  Here we are sending 8 bytes on a periodic CAN frame, with the first byte being a counter which goes from 0 to 3 back back to 0.  Bytes 2 through 8 come from the Payload control.  The VI will add 10 frames to the queue, and then periodically check to see how many are in the queue.  If the number of pending items is less than 10 this VI adds 10 more.  One downside of this is that a value change on the Payload may not be seen on the CAN bus until the remaining pending items are sent.  There are some more advanced techniques like flushing the buffer on a value change of the other payload bytes but then there is more work to know what the counter value should be.

Payload CRC

As discussed in Part 2 the CAN frame has a very basic CRC built in which ensures that the message is received properly.  In some automotive applications an additional CRC can be added to ensure the data being received is correct and from a device that is still communicating.  In all instances I've seen this implemented this is done with the last byte of the payload.  The particular algorithm for generating the CRC can vary, but the SAE J1850 standard uses a polynomial of 0x1D, so we will use that for this example.

With this in mind we have another reason we may need to calculate the data for each frame.  If we have an incrementing counter, and we have a CRC, then the CRC is going to change with every frame sent, since the counter will change with every frame sent.  In addition to this the payload may change, and the CRC will need to change when that occurs too.  Luckily there's an example for that too.  This is very similar to the previous example except the last byte is used for the CRC, and the CRC is calculated using a VI found in the XNet Tools package which I originally found on the NI forums here.

This is pretty similar to the last example except there we take bytes 1 through 7, perform a CRC, and then use that as the last byte in the payload.

Non-XNet Solution

So lets say you don't have XNet hardware, and can't push frames into a queue that get sent out on hardware timing.  Does this mean you can't perform an incrementing counter and CRC?  No it just means that the timing needs to be done in software, and as a result a decent amount of jitter will be seen.  Your code will typically sit in a small while loop waiting for some amount of time to go by, and then send the next frame.  On Windows you can expect jitter on the order of 10ms or so.  You may get lucky and have no jitter, but because Windows is a general purpose operating system you can never guarantee timing.  If your requirement is to send out a frame every 100ms or more software timing will probably work just fine.

Part 10

Part 10 describes an undocumented, but awesome feature of the XNet hardware.  This feature allows for uploading code to the hardware and having it execute at run-time.  The only good example I have of this is to perform CRC and Counter incrementing functionality.  But what this means is this functionality can be handled completelly on the XNet hardware and requires no periodic functions to run in your LabVIEW program.  This frees up CPU resources, and makes coding easier.  This feature could have other benefits in the future if NI ever chooses to fully support it.

CAN Part 8 - ISO 15765, KWP2000, ODB, and UDS (Automotive Diagnostic Command Set)

01/21/2018 10:36 PM

In the earlier parts of this blog we CAN hardware, software, APIs, databases, CAN Signals and CAN Frames.  In this part we are going to talk more about one of the software techniques that has been developed to get around some of the technical limitations to CAN to help allow for other types of data, with larger payloads, to be sent and received.

Standard CAN Example

As defined by the CAN 2.0 spec, a standard or extended CAN Frame consists of up to 8 bytes of payload, in 1 byte increments.  This is a hard limitation and cannot be changed, without changing to a new specification like CAN FD which also requires new hardware from multiple devices.  But often times when we start getting into automotive ECU development we find ourselves wanting to use CAN in ways that maybe it wasn't intended to be used for.  For example the CAN data we've been talking about so far has primarily been periodic information.  A device may send out the status of an analog input every 100ms by sending out a single frame that contains this signal information.  This information is great for looking at values that may change rapidly over time, and having a waveform of all data at small time increments like that is very useful.

Engine Trouble Codes Example (DTC)

But lets say for a moment that I want to use CAN for something else.  Let say for instance my check engine light has turned on in my car and I want to know why.  An automotive technician will plug in a device on the CAN bus and it will ask the ECU what is wrong.  The ECU will then respond and tell the scanner what the list of things are that are currently wrong, and a list of things that were wrong but aren't any more, to give some history of issues to the technician.  These trouble codes are known as Diagnostic Trouble Code (DTC), and aren't periodically sent, and instead only are sent when another device asks it to.  DTCs are the kind of data that should not be sent every 100ms because over the life of the ECU there is a small chance that a device that wants to know what the DTCs are, is even listening.  So this type of data is sent only on the event that it was requested.  Standard and Extended CAN data can be sent this way but there is another issue with DTCs.

The list of potential DTCs any ECU can have set could be in the hundreds.  And we cannot know how many are set, or which ones are set, until the ECU tells us.  This causes the response from the ECU to vary the amount of data it is going to send back depending on other conditions, and it will likely need to be able to send back more than just 8 bytes of data which a CAN frame is limited to.

ISO 15765 Multi Frame Messaging

This is where the ISO 15765 protocol comes in.  The protocol is used in places where more than 8 bytes of data need to be received, or more than 8 bytes of data needs to be sent as a response.  It is also used in places were the data that needs to be sent or received is not the kind of data that should be blindly broadcasted on a network autonomously.

How does it work?

The basics of the 15765 are pretty simple.  A device will send out a single frame telling another device that it has something it wants to say.  Now if the thing it wants to say is small in size, it can be all contained in that one frame.  But often times we need to send more data so the sender will wait for the receiver to get the data, and then respond.  In the response the receiver will state how many frames the sender can send at once, and how much time can be between each frame.  Older CAN devices had a small buffer size, and wouldn't be able to receive data too fast.  So the sender is supposed to receive this one frame, known as a flow control, and then send back more data in with the timing specified.  This helps prevent the device receiving the data from being overloaded, since the sender will only send the specified number of frames requested.  Once all of the data gets received, the receiving device will act on it, and then send a reply back.  Just like the request, the response can also be a single frame, or multiple frames.  If it is multiple frames then it will send the first frame, and then the original sender device will send back a frame stating how many frames it can receive, and what timing should be between them.  These steps are a bit confusing so here is a flow diagram explaining it, generated using the NI State Diagram Toolkit.  The state machine starts on the green node, and ends on the red one.

Looking at this flow diagram things might be a bit confusing, so lets break it down a bit more and talk about the various components and checks that go into using raw frames for performing 15765 queries.


So in Part 2 we first talked about CAN Frames and mentioned the payload limitation, as well as the fact that each frame has an ID.  Well when using ISO 15765 there are only two IDs ever used.  One ID is for the request and one ID is for the reply.  The ID performing the request is known as the Command Receive Object (CRO), and the ID for the response is known as the Data Transmission Object (DTO).

This means that all requests from other devices on the bus will have the same IDs, and the same priority.  This also means that the reply sent back will go to all devices on the bus, and it is the the responsibility of the device to look at the payload sent back to determine if the reply it found was intended for that device, or if the this response was to another devices request that was issued.  On a normal automotive CAN bus these 15765 messages aren't going out often, and as mentioned with our DTC example is something that is useful for debugging but isn't intended to be part of normal operation.  As a result this issue of multiple requester at once is probably a rare case and in most situations every reply you see to an ISO 15765 message, is going to be from a request your device issued.

The CAN IDs used in 15765 can vary from device to device, and will be specific to your device.  It is common to have different request and response IDs for each device on the bus so this information needs to be looked up in the documentation for the device you are trying to talk to.  In the following examples we will use CAN ID 0x70 for the request and 0x7F for the response.

Simple Example

The following is a simple example where we send a small amount of request data, and we receive a small reply following the state machine from above.

Write Single Frame

At the start of our state machine we have a state where we either write a Single Frame if our payload is less than or equal to 7 bytes, or we will write the first frame in many if it is more.  Lets start with writing the single frame and how it looks.  Lets say we intend on writing the following 3 bytes of data 0x22 00 02, when we do this using 15765 our Single Frame we send will look like this:

--> ID 0x70 Payload 0x03 22 00 02 00 00 00 00

The padding of the frame to 8 bytes isn't always required and from now on will not be included but some hardware may expect it.  The rest of the payload is pretty simple.  The first byte represents the number of bytes in our transmission.  Because this first byte always says how many bytes to expect, it leaves us with only 7 bytes of usable payload.

Some messages don't require a response, and if we are sending a Single Frame, and we don't care about the response we are done.  If we do care about the response we then go and start reading frames looking for the response in the Read Reply state.

Read Reply

So in our example we have successfully sent one frame which contained all the data we wanted to send.  The next step is to look for a reply.  The only reply we care about, is one on the CAN ID associated with a response and in our case is 0x7F.  All other frames can be ignored.  If too much times goes on and we haven't seen a frame with ID 0x7F we go to Done where we can throw an error.  This timeout is usually on the order of 1 second but can vary depending on hardware.  Assuming we do see a frame with ID 0x7F the next step is to identify what kind of frame it is.

Process Read (Single Request)

So we sent a frame, and we just got one back that is in response to what we sent.  Now lets identify what kind of frame it is in Process Read state.  Just like the first step in our state machine, the reply could be a single frame taking 7 or less bytes, or it may need to be multiple frames.  For simplicity lets say the reply is also 7 bytes or less and consists of the following data, 0x01 02 03 04.  As a result the reply may look something like this:

<-- ID 0x7F Payload 0x62 04 01 02 03 04

The first byte of the response is 0x62 because the first byte of the request was 0x22, and the response needs to be the same as the request, but with 0x40 added.  This helps us know the response we found was to a request we issued.  If the first byte was anything else we know the message was not for us.  The only other valid first byte that is for us is 0x7F which is a negative response which we'll get into later.

So 0x62 means the reply is for us, and 0x04 means we have 4 bytes of data to receive, and then after that we have the 4 bytes of data.  So in this example we sent the three bytes of data 0x22 00 02, and we received four bytes of data 0x01 02 03 04.  Using another protocol on top of 15765 known as UDS (ISO 14229), this example data corresponds to asking the device to read an identifier (memory address) 0x00 02, and the device returning the memory at that address which was 0x01 02 03 04

--> ID 0x70 Payload 0x03 22 00 02 00 00 00 00    #Request to read data at address 0x00 02
<-- ID 0x7F Payload 0x62 04 01 02 03 04              #Response data at that address 0x01 02 03 04

 Negative Response

If we got a negative response from the device it would look something like this:

<-- ID 0x7F Payload 0x7F 22 11

The 0x7F means this is a negative response, to the request 0x22, and the status of the response is 0x11.  There can be many custom response codes but there are a set of them that are mostly standard.

Negative Response Table
Response CodeResponse
0x10General reject
0x11Service not supported
0x12Sub-Function not supported
0x13Incorrect message length or invalid format
0x14Response too long
0x21Busy repeat request
0x22Conditions not correct
0x24Request sequence error
0x25No response from sub-net component
0x26Failure prevents execution of requested action
0x31Request out of range
0x33Security access denied
0x35Invalid key
0x36Exceeded number of attempts
0x37Required time delay not expired
0x38 - 0x4FReserved by Extended Data Link Security
0x70Upload/Download not accepted
0x71Transfer data suspended
0x72General programming failure
0x73Wrong Block Sequence Counter
0x78Request correctly receive, but response is pending
0x7ESub-Function not supported in active session
0x7FService not supported in active session

Looking at the state machine the only other option is needing to reissue the request.  If this happens the sender should send the request again.  One other negative response that doesn't end the state machine is if the device asked for more time 0x78.  If this negative response is seen  the receiver should reset the timeout value and continue looking for the next positive, or negative response.

More Complicated Example

The following example is a bit more complicated and will involve sending a larger amount of request data, and we receive a larger amount of data in a reply, again using the state machine from earlier.

Write First Frame

The example listed above is a very simple one.  We send a single frame, and we get a single frame.  but 15765 is more useful when sending large amounts of data.  So lets walk through an example of sending more data.  If we want to send more than 7 bytes of data we still need to start by sending the First Frame and then we will send more when we are ready.  Lets say for instance we want to write the following 17 bytes of data 0x2E 00 02 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D, our First Frame will look like this:

--> ID 0x70 Payload 0x10 11 2E 00 02 00 01 02

Here the first two bytes are used telling the receiver what the transmission will contain.  The second nibble in the first byte of the payload being a 1 is telling the receiver that there is more data than just this one frame.  This leave the remaining lower nibble, and 2nd byte to be used to tell the receiver how many bytes to expect and in our case it is 0x00 11 or decimal 17 which is the full payload we need to send.  Since the first two bytes of the payload are taken by the number of bytes to be sent, this leaves us with only 6 bytes to send usable data through, and so we take the first 6 bytes of our 17 bytes and send it.

Reply is Flow Control

So after sending the first frame, we will wait reading new frames just like the previous example, but we expect to see a flow control frame.  This is a frame that tells us how many frames the device can read at once, and how much time needs to be between each one.  The reply will look something like this:

<-- ID 0x7F Payload 0x30 05 0A

Here the 0x30 tells us this is a flow control message, 0x05 means the device can accept the next 5 frames before needing to reissue the flow control.  This helps so that we don't overload the device and we know that we are sending data to a device that is still listening.  And 0x0A is the separation time needed between each frame.  The units of this are in milliseconds from 0 to 127, and in microseconds from 100 to 900.  This doesn't need to be precise and is a lower bounds, so the device should be fine if we send them a little slower than that, but we shouldn't send it faster than requested.  A zero for the number of frames means just send them all, and a zero for the separation time means no delay between frames is needed.

Write Chunk of Data

After we sent the first frame, and we received the flow control response, we are now able to start sending chunks of data.  We need to abide by the flow control rates, and number of frames.  In this case we are only going to need to send 2 more frames and the device said we can send up to 5, as long as we separate them by 10 milliseconds.  Because of that our next two frames we write will look like this:

--> ID 0x70 Payload 0x21 03 04 05 06 07 08 09
--> ID 0x70 Payload 0x22 0A 0B 0C 0D

The first byte in the payload here goes from 0x21 and increments to 0x2F then back around to 0x21 with each frame sent in this chunk.  In our case we only needed to send 2 frames.

Read First Frame Reply

So we sent some data, waited for the device to reply, then we were able to send the rest of our payload.  Now we need go into perform similar work as we did in the first example.  We continue to read data looking for a reply.  Just as in the earlier example the reply may tell us to re-transmit a request, wait longer for a reply, or get the reply.  The reply can be a single frame as in the previous example, or we can have a multi frame reply, just like our multi frame request.  We've already seen a single frame reply example so lets look at the multi frame reply.  In this example lets assume the device wants to reply with the following 21 bytes of data 0x6E 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14.  This will start with the first few bytes of the payload as before:

<-- ID 0x7F Payload 0x10 15 6E 01 02 03 04 05

Here we see the second nibble telling us this is the first frame of many because it is a 1, and the first nibble and second byte is 0x00 15 telling us there are 21 bytes total to receive.  Then the remaining 6 bytes is the first 6 of the reply.

Write Flow Control

So we've sent our request, and we got the first frame of the reply, but now we need to tell the device how many frames it can send us, and how much time can be between each frame.  In most cases our hardware can just read as many frames as possible since they will sit in our hardware buffer, and we don't really need any time between.  But just for an example we will tell the device we can only read 4 frames at a time, and we need 20 milliseconds between each:

--> ID 0x70 Payload 0x30 04 14

After we send this the device is free to send up to 4 frames with 20 milliseconds between them.  To tell the device to send all frames with no delay replace the last two bytes to 0.

Read Consecutive

So we sent a request, and we got the first 6 bytes of the reply, and we sent the flow control telling the device to send the rest which will look like this, which mirrors how the request message sent multiple frames earlier:

<-- ID 0x7F Payload 0x21 06 07 08 09 0A 0B 0C
<-- ID 0x7F Payload 0x22 0D 0E 0F 10 11 12 13
<-- ID 0x7F Payload 0x23 14

After this it is the responsibility of our software to concatenate all of the data into the request and response.  Here is the full transaction that takes place:

--> ID 0x70 Payload 0x10 11 2E 00 02 00 01 02     #First frame of multiple in request
<-- ID 0x7F Payload 0x30 05 0A                             #Flow control telling us how to send the request
--> ID 0x70 Payload 0x21 03 04 05 06 07 08 09     #More data in request
--> ID 0x70 Payload 0x22 0A 0B 0C 0D                 #More data in request
<-- ID 0x7F Payload 0x10 15 6E 01 02 03 04 05     #First frame of multiple in reply
--> ID 0x70 Payload 0x30 04 14                               #Flow control telling how we can receive data
<-- ID 0x7F Payload 0x21 06 07 08 09 0A 0B 0C   #More data in response
<-- ID 0x7F Payload 0x22 0D 0E 0F 10 11 12 13    #More data in response
<-- ID 0x7F Payload 0x23 14                                    #More data in response

(If you made it this far congratulations, that was a very large wall of text in those two basic examples.)

Other Protocols

The ISO 15765 just defines how to read and write multi-frame data, but doesn't define what that data is.  The Unified Diagnostic Services (UDS), Keyword Protocol 2000 (KWP2000), and On-Board Diagnostic (OBD) and three software layers that each sit on ISO 15765 to perform operations like reading and clearing DTCs.

Automotive Diagnostic Command Set (ADCS)

The NI toolkit ADCS is basically the software implementation of ISO 15765, KWP2000, OBD, and UDS for NI-CAN and NI-XNet devices.  This is fine if you have these hardware options, but the software to implement this in any other language is laid out before you.  So I decided to take the ADCS toolkit, and replace the core calls to the NI DLL driver, which is locked down, and replace it with my G implementation.  I posted this idea on the Idea Exchange.  I also ran into several bugs with the toolkit and getting support wasn't easy, so I eventually posted the basic G implementation of the toolkit for NI-CAN hardware here.  With the G implementation before us, all that is needed to this functionality in other hardware is to just replace the CAN Read, and CAN Write functions with the hardware API of your choice.  This means that ISO 15765, and really the ADCS functionality is now available for any CAN hardware in LabVIEW.  I've created a set of code and wrappers and inlcuded them in the Hooovahh CAN ISO 15765 package, which works with the CAN Drivers package for 3rd party devices from earlier.

So with this newly created G code and palette, what steps are needed to read the DTCs on an ECU, and then clear the DTCs?  Take a look at the following example.

Here we open the first Vector port, initialize the protocol based on the request and reply ID, read the DTCs, Clear DTCs, then close the references  There are some default values on some subVIs but the values used are the most common.  All of the ISO15765 protocol, and the UDS protocol that sits on top of it is all available in the G implementation inside those VIs.  These protocols and APIs work with any KVaser, Vector, or Intrepid CAN hardware device, since it uses the Hooovahh CAN Drivers package from earlier to perform the raw CAN frame reading and writing.  The hardware drivers aren't wrapped into a nice hardware abstraction class, but they probably should be, I just didn't get around to it.  Regardless the point of this code is not to give a working solution for your project, but instead to demonstrate how ISO 15765 operates, and demonstrate that it can be used on any CAN hardware as long as you can read and write raw CAN frames.  This means adding support for other hardware types like NI-CAN, NI-XNet, or any other device can be done without too much extra work.

Part 9...

In this blog series we've covered a lot of information.  From hardware, to APIs, to CAN Frames, CAN Signals, databases, advanced XNet features, and now a software layer on top of CAN to help facilitate multi frame data, and event based communication.  What else could we possibly talk about?  The subjects are now going to get a bit more specific.  So in Part 9 we will have a quick discussion on the availability of what I call the bucket filling problem in XNet, and how to handle functions like CRC, and incrementing counter signals.

All Posts