Skip to content

Introduction to the SAE J1939 Standard

In the early days of controller area networks (CAN), every device manufacturer interpreted CAN frames in its own proprietary way. When you changed the engine of a harvester from Volvo to MAN, you would have to reimplement the communication with the engine from scratch. Fendt tractors wouldn’t be able to communicate with John Deere implements and vice versa. The J1939 standard brought order into this Babylonian chaos and reduced the development efforts significantly.


For the engine manufacturer, the CAN frame (ID # Payload)

0cf00400 # 62c54928421307d3

may provide information about the engine speed and torque. The manufacturer of the drive ECU may interpret the frame as the distances covered on the field and road. For the trailer manufacturer, the frame may contain information about the weight of the load and the filling level of the trailer. This Babylonian chaos makes the communication between ECUs, implements and vehicles close to impossible.

This communication nightmare was the reality in the early days of controller area networks (CANs) – until the manufacturers of commercial vehicles decided to standardise the communication over CAN in vehicles. This was the birth of the J1939 standard published by the Society of Automotive Engineers (SAE).

The J1939 standard gives a unique interpretation to the CAN frame above. Actually, the engine manufacturer got it right. The frame contains information about the engine speed and torque. The electronic control unit (ECU) of the engine sends the frame to all other ECUs connected to the same CAN bus.

The standard document J1939-21 defines the structure of the frame ID and the payload. The middle two bytes of the frame ID (0xf004 above) act as a key into a huge table, the J1939 Digital Annex (J1939DA, for short). For each key, the table defines which bit ranges correspond to which parameters. The table allows you to decode the payload into parameter values like the current engine speed and torque.

The J1939 standard defines more than 1500 frames and more than 100 ECU IDs for highway, agricultural, forestry, construction and marine equipment. It enables the communication between ECUs, implements and vehicles from different manufacturers. It also allows manufacturers to add proprietary frames.

J1939 Frames

J1939 frames are CAN frames in the Extended Frame Format (EFF), where the frame ID has 29 bits. The payload contains at most 8 bytes. Almost all J1939 frames have a size of excactly 8 bytes.

CAN frames in the Base Frame Format (BFF), where the frame ID has only 11 bits, are tolerated on the J1939 network as proprietary messages. Legacy CAN devices can talk to each other or to J1939 devices over a J1939 network. So, you can migrate a legacy CAN network device by device to a J1939 network.

J1939 networks do not support CAN FD, which has flexible data rates and payloads with more than 8 bytes. If CAN FD devices are on a J1939 network, they must not use their FD capabilities.

The 29-bit ID of a J1939 frame has the following structure.

ID of J1939 frame

Bits 0-7 (byte 0) hold the source address (SA) of the device that sent the frame. J1939DA defines the source addresses of common devices. For example, the engine has the source address 0x00, the steering controller has 0x13, the body controller has 0x21 and the terminal has 0x28.

Bits 8-23 (bytes 1 and 2) contain the parameter group number (PGN). The PGN defines which parameters belong to the group and which bit range of the 64-bit payload encodes which parameter. The PGN acts as key into a table of payload definitions. The table SPNs & PGNs in J1939DA maps the standardised PGNs to their payload definitions. You’ll see detailed examples in the sections about broadcast and peer-to-peer frames. Device manufacturers provide similar tables for proprietary PGNs.

The PGN consists of the PDU Specific (PS) and the PDU Format (PF). The PS is encoded by the bits 8-15 and the PF by the bits 16-23. If PF is in the range from 0 to 239, the frame is a PDU-Format-1 frame or a peer-to-peer frame. PS then holds the destination address (DA), that is, the address of the device receiving the frame. This leaves 240 PGNs for peer-to-peer frames. PGN 239 is for proprietary use.

If PF is in the range from 240 to 255, the frame is a PDU-Format-2 frame or a broadcast frame. The combination of PF and PS yields the PGN. The 4096 numbers from 0xf000 to 0xffff are valid broadcast PGNs. The PGN with PF equal to 255 is for proprietary use.

Bit 24 is the Data Page (DP). You use the DP bit to double the number of available PGNs. Bit 25, the Extended Data Page (EDP), is always 0 for J1939 frames.

Bits 26-28 hold the priority of a J1939 frame. The priority ranges from 0 to 7, where 0 is the highest priority and 7 the lowest priority. The default priority for informational, proprietary, request and acknowledgement frames is 6. The default priority for control frames (e.g., speeding up or slowing down the vehicle) is 3.

Bits 29-31 are set to 0, because they are not needed for a 29-bit frame ID stored in a 32-bit integer.

J1939 Broadcast Frames

When an ECU sends a broadcast frame, all other ECUs on the same CAN bus receive the frame. They can react to the frame or ignore it. For example, the terminal can display the engine speed. An ECU can switch on some lights or rotate the cutting blades faster. ECUs send broadcast frames at regular intervals (e.g. every 20 ms) or when the parameter values of the frames changed.

An EEC1 frame (EEC1 = Electronic Engine Controller 1) could look as follows:

0cf00400 # 62c54928421307d3

You first decode the frame ID 0x0cf00400 into the priority 3, the data page 0 (DP), the parameter group number 0xf004 (PGN) and the source address 0x00 (SA). You then search for the PGN 0xf004 (61444 decimal) in the huge Excel sheet J1939DA (column PGN of the tab page SPNs & PGNs). You find the following eight lines for the PGN 61444 defining the payload of the frame as follows.

Bits     Name                  Unit   Factor   Offset   Range
0..3     Torque mode           -      1.0      0        0..15
4..7     Additional torque     %      0.125    0        0..0.875
8..15    Driver demand torque  %      1.0      -125     -125..125
16..23   Actual torque         %      1.0      -125     -125..125
24..39   Engine speed          rpm    0.125    0        0..8031.875
40..47   SA of controller      -      1.0      0        0..255
48..51   Starter mode          -      1.0      0        0..15
52..55   Unused                -      1.0      0        0
56..63   Engine demand torque  %      1.0      -125     -125..125

Note that the above table only shows the most relevant columns and that I condensed the information for readability.

The payload is encoded in little-endian byte order from byte 0 to byte 7. Every byte is in big-endian bit order.

Bytes:  0     1      2       3       4       5       6       7
Bits:   7..0  15..8  23..16  31..24  39..32  47..40  55..48  63..56

Byte 0 of the example payload is 0x62, where 0x2 corresponds to bits 0..3 and 0xe to bits 4..7. Hence, the raw values of the torque mode and of the additional torque are 2 and 6, respectively. The display value is calculated with the formula

displayValue = factor * rawValue + offset

This yields the following results for the first two parameters:

torqueMode = 2 * 1 + 0 = 2
additionalTorque = 6 * 0.125 % + 0 % = 0.75 %

The maximum admissible value for the additional torque is 0.875 = 7 * 0.125. Hence, the raw values from 8 to 15, which correspond to the display values from 1.0 to 1.875 in steps of 0.125, are not admissible. Nothing in the J1939 standard forbids ECUs to send the out-of-range raw values. However, ECUs shouldn’t do this. It makes the life of the receiving ECUs so much easier.

Bytes 1 and 2 are interesting, because they have non-zero offsets. Here is the math:

driverDemandTorque = 197 * 1.0 % - 125.0 % =   72 %   # 197 = 0xc2
actualTorque       =  73 * 1.0 % - 125.0 %  = -52 %   #  73 = 0x49

Both parameters show how to represent negative numbers. The range m..n is translated to the range 0..n-m where offset = -m. In the example above, the range -125..125 is translated to 0..250 where offset = 125. The raw value is always an unsigned integer. You represent negative integers with the help of the offset. And, you turn integers into floating-point numbers by multiplying the raw value with the factor.

Bytes 3 and 4 with the value 2842 represent the 2-byte engine speed. The payload encodes multi-byte numbers in little-endian format. Humans prefer numbers in big-endian format. So, the engine speed becomes 0x4228 or 16936 decimal. The display value is 16936 * 0.125 rpm + 0 rpm = 2117 rpm.

The remaining three parameters are easy to calculate. Bits 4..7 of byte 6 are not used. So, the value is ignored. The J1939 standard recommends to avoid unused gaps to maximise the data throughput on the 250Kbps CAN bus.

Here is the decoding of all parameters:

torqueMode         = 2 * 1 + 0         = 2         # 2 in 0x62
additionalTorque   = 6 * 0.125 + 0     = 0.75 %    # 6 in 0x62
driverDemandTorque = 197 * 1.0 - 125.0 = 72 %      # 197 = 0xc2
actualTorque       = 73 * 1.0 - 125.0  = -52 %     # 73 = 0x49
engineSpeed        = 16936 * 0.125 + 0 = 2117 rpm  # 16936 = 0x4228  
controllerSA       = 19 * 1.0 + 0      = 19        # 19 = 0x13
starterMode        = 7 * 1.0 + 0       = 7         # 7 in 0x07
engineDemandTorque = 211 * 1.0 - 125   = 86        # 211 = 0xd3

The J1939 standard allows the bit ranges of parameters to cross byte boundaries. Bit ranges like 4..11 or 17..41 are perfectly OK although rare.

J1939 Peer-To-Peer Frames

The terminal tells the header ECU of a maize harvester to increase or decrease the cutting height. The terminal sends a peer-to-peer request frame with the new height to the ECU. The ECU stores the new height and acknowledges the terminal’s request with a peer-to-peer response frame. The ECU gradually adjusts the height of the header to its new value.

The terminal can request from another ECU to write a given value to a parameter or to read the value of a given parameter. The request and response are similar to remote function calls over the CAN network. These request-response scenarios are best implemented with J1939’s Group Functions.

For changing the cutting height, the terminal sends the write-request

18eff828 # 0203029103000000

to the header ECU. Decoding the frame ID yields a priority of 6 (right-shift 0x18 by 2 bits), a PF of 0xef (239 decimal), a PS of 0xf8 and a source address of 0x28. As the PF is less than 240, the frame is a peer-to-peer frame and the PS is the destination address (DA). The header ECU has the proprietary address 0xf8. The terminal has the standard address 0x28. 239 is the PF of a proprietary frame.

In short, the terminal sends a proprietary peer-to-peer message with priority 6 to the header ECU. For a proprietary message, you must define the structure of the payload yourself. Here is a common definition for read and write requests. Note that the first column denotes bytes now.

Bytes    Name                  Unit   Factor   Offset   Range
0        Command               -      1.0      0        0..255
1..2     Parameter ID          -      1.0      0        0..2^16-1
3..6     Parameter value       unit   factor   offset   range
7        Unused                -      1.0      0        0

The command (byte 0) has value 0x01 for read requests and value 0x02 for write requests. The parameter ID (bytes 1..2) is a 16-bit unsigned integer and denotes the parameter on the remote ECU to which the read or write action is applied. For write requests, the parameter value (bytes 3..6) holds the value that the destination ECU shall assign to the parameter with the given ID. For read requests, the parameter value is set to 0, because it is not used. Parameter ID and value are given in little-endian format.

Unit, factor and offset differ from parameter to parameter. For example, the cutting height is given in tenths of millimetres and the AC temperature in centigrades.

Name             Unit  Factor  Offset  Range
cuttingHeight    mm    0.1     0       30.0..95.0 mm
acTemperature    °C    1       0       12..26 °C

You’ll specify the parameters in a table similar to J1939DA. More than 1000 parameters are fairly normal for harvesters.

Bytes    Name                  RawValue      DisplayValue
0        Command               02            Write request
1..2     Parameter ID          0302          0x0203 -> 515
3..6     Parameter value       91030000      0x0391 -> 91.3 mm
7        Unused                0             Ignored

The terminal asks the header ECU to set the value of parameter 515 to 91.3 mm. The header ECU checks whether the parameter 515 exists, whether the terminal has the permission to change this parameter and whether a parameter change is possible in its current state. If the ECU succeeds in changing the parameter, it will respond with the peer-to-peer frame

18ef28f8 # 0203029103000000

Note that the destination and source addresses are switched compared to the request frame. The header ECU sends a message to the terminal. Also note that byte 7 now holds the error code, where 0x00 means no error.

If the ECU fails to change the parameter, it will respond with the peer-to-peer frame

18ef28f8 # 0203029103000001

The error code 0x01 in byte 7 could mean that the header ECU was in a state, where it could not change the value. Command, parameter ID and value are the same as in the request. Thus, the terminal can figure out which request caused the error and try to fix the problem.

As the CAN bus adheres to the publish-subscribe pattern, all ECUs receive the peer-to-peer frames. The J1939 standard recommends that ECUs ignore these frames, if they are not the destination.

Also Interesting

This post is a spin-off from my talk A Deep Dive into Controller Area Networks with Qt at Qt World Summit 2019. I use J1939 frames in my two articles Speaking CAN: Request and Response and Speaking CAN: Write Buffer Overflow – without explaining them properly. This post hopefully closes this gap.


  • SAE International, Data Link Layer J1939/21_201810, October 2018, Price: 83 USD. This document defines the structure of the frame ID and payload and the communication in a J1939 network.
  • SAE International, J1939 Digital Annex J1939DA_201910, October 2019, Price: 250 USD. This document is a huge Excel sheet specifying all the standardised frames.

3 thoughts on “Introduction to the SAE J1939 Standard”

  1. Can you check this part: “18eff828 # 0203029103000000
    …to the header ECU. Decoding the frame ID yields a priority of 6 (right-shift 0x18 by 2 bits)”

    I can’t make sense of the priority. If you shift 18 (HEX) or 10010 (BINARY), the result is 100 (BINARY) which is 4 (HEX).

Leave a Reply

Your email address will not be published. Required fields are marked *