Next edition of  “Get Noticed!” started, and it’s my third attempt to write a blog in scope of one year, you know what they say, third time’s a charm. Little inspired by idea in post by fellow contender, I’m putting X amount for first post a week, and twice X for second in week, to “pleasure budget” that I can spend without remorse. We’ll see where that leads me. The goal of the project is to create “yet undefined” set of tools to make my work with LIN, a little easier and faster. The bread idea is to have tool generating .NET/F# client based on LDF (a little more on that in next post). Let’s start with short introduction to LIN.

What is LIN?

LIN (Local Interconnect Network) is a low-end and low-cost,  single wire Serial Network Protocol that is used in automotive networks to communicate between vehicle components. It consists of 1 master and up to 15 slaves.

LIN message

Each message is identified by ID. Valid IDs are within range <0,63>, where 0 to 56 are used for signal carrying, 60 and 61 for diagnostics, 62 for user defined extensions, and 64 reserved for future enhancements.  on lower 6 bits, while the two upper bits contain parity.

Parity calculation

let calcPID ID =
  let ID0 = ID &&& 1uy
  let ID1 = ID >>> 1 &&& 1uy 
  let ID2 = ID >>> 2 &&& 1uy 
  let ID3 = ID >>> 3 &&& 1uy 
  let ID4 = ID >>> 4 &&& 1uy 
  let ID5 = ID >>> 5 &&& 1uy 
  let P0 = ID0 ^^^ ID1 ^^^ ID2 ^^^ ID4
  let P1 = ~~~(ID1 ^^^ ID3 ^^^ ID4 ^^^ ID5) &&& 1uy
  ID ||| (P0 <<< 6) ||| (P1 <<< 7)


Message data consists from at least 1 byte and up to 8, and checksum. LIN 1.x uses Classic Checksum, while LIN 2.x uses Enhanced Checksum with the exceptions of messages with IDs from 60 to 63 which should be calculated with Classic Checksum.

Classic and Enhanced Checksum

Classic checksum is calculated using only message data, while in Enhanced Checksum we also include Protected ID, In order to calculate checksum we sum all bytes, subtracting  255 each time sum is greater or equal to 256, and then invert resulting value.

let carry255 a b = 
  match a + (b |> uint16) with 
    | s when s > 255us -> s-255us 
    | s -> s
let calcEnhancedChecksum pid = List.fold carry255 (pid |> uint16) >> byte
let calcClassicChecksum = calcEnhancedChecksum 0uy 

Sample Device

Before we continue, let’s describe imaginary example device will be working with further. Let’s say it’s comfort system with following functions:

  • run massage, 12 air cells in 2 rows, 6 cells each. In order to run it we select:
    • one of 3 predefined programs – varying in order of cell inflating/deflating
    • one of 3 intensity levels
    • one of 5 speed levels
  • inflate, deflate:
    • 2 lumbar cells – operating in 4 modes FORWARD (inflate both lumbar cells), BACKWARD (deflate both lumbar cells), UP (inflate lumbar 1 and deflate lumbar 2), DOWN (deflate lumbar 1 and inflate lumbar 2)
    • 2 cushions bolsters – inflate/deflate both at the same time
    • 2 back bolsters – inflate/deflate both at the same time

To give a picture how such a system could look in reality, it could consist of:

  • pump
  • 2 valve blocks, each having 4 bidirectional valves inflate+deflate
  • 1 valve block, having 6 bidirectional valves inflate+deflate

where 6VB would be LIN slave, and master for both 4VB and pump, in other words, we would only communicate with 6VB, while 6VB would then delegate “some work” to two 4VB and pump, on a protocol beyond our concern.

For the convenience of our diagnostics let’s add few more functions to the system:

  • massage status
    • is massage running
    • currently inflating/deflating air cells
  • lumbar and bolster status:
    • is pump running
    • is valve working
  • direct operations on pump and valves (to give example when such would come handy, we could need to test power consumption while operating pump and valves):
    • pump speed 0-100%
    • inflate/deflate singe valve in each valve block