j1939decode icon indicating copy to clipboard operation
j1939decode copied to clipboard

J1939 decode C library

J1939 decode library

Decode J1939 CAN bus messages into a JSON string representation of what the payload data means based on the SAE standard.

This library by default will attempt to read the file J1939db.json from the current directory to load in the J1939 database. If this file cannot be read, J1939 decoding will not be possible. This file name and path can changed by redefining the J1939DECODE_DB C macro to point to a different filename.

See below for how to generate the J1939 database file.

Building

Create a build directory relative to project root, run cmake and then make.

mkdir -p build
cd build
cmake ..
make -j

Cleaning

Remove generated directories: build/

Install

To install first build the library, then run sudo make install from within the build directory.

Uninstall

To uninstall run sudo make uninstall from within the build directory.

Testing

Unit tests make use of the Unity testing framework which is used by the Ceedling build system.

To start all tests run ceedling test:all.

Library usage

Call j1939decode_init() first before calling j1939decode_to_json().

When done, call j1939decode_deinit() to free memory allocated by j1939decode_init() and also remember to free the string pointer returned by j1939decode_to_json().

User-supplied log handler

j1939decode_set_log_fn() can be used to set a user-supplied log handler function. j1939decode_set_log_fn() should be called before calling j1939decode_init() since library initialization may generate log messages.

The replacement log handler should have the following signature:

void custom_log_handler(const char * msg);

If NULL is supplied for j1939decode_set_log_fn or it is not called at all, then the default logger function will be used, which prints all log messages to stderr.

J1939 database file generation

The J1939db.json database file is a JSON formatted file that contains all of the PGN, SPN, and SA lookup data needed for decoding J1939 messages.

This database file can be generated using the create_j1939db-json.py script provided by pretty_j1939 repository. A copy of the SAE J1939 Digital Annex spreadsheet is required.

For example:

create_j1939db-json.py -f J1939DA_201704.xls -w J1939db.json

JSON format

The output JSON string generated by j1939decode_to_json() contains the following fields. Where possible, the decoder will attempt to decode the CAN message according to the J1939 protocol spec.

  • "ID": CAN identifier
  • "Priority": message priority
  • "PGN": parameter group number
  • "PGNName": parameter group number descriptive name
  • "SA": source address
  • "SAName": source address descriptive name
  • "DLC": data length code
  • "DataRaw": array of CAN data bytes, MSB first
  • "Decoded": boolean indicating if J1939 decoding was successful (i.e. one or more SPNs decoded)
  • "SPNs": object containing all decoded suspect parameter numbers associated with the given PGN

SPN objects use their SPN number as their name, and contain a sub-object with members specific to that SPN.

The following members are found within an SPN sub-object. All members relate to the specific SPN under which they are found.

  • "Name": suspect parameter number descriptive name
  • "DataRange": valid data value range as a human-readable string
  • "OperationalRange": valid data value range (in decoded data value units)
  • "OperationalHigh": maximum valid data value (in decoded data value units)
  • "OperationalLow": minimum valid data value (in decoded data value units)
  • "StartBit": starting bit number of data within 64 bit data field
  • "SPNLength": length of data in bits
  • "Resolution": scaling multiplier
  • "Offset": linear offset
  • "ValueRaw": raw data value without resolution and offset applied
  • "ValueDecoded": decoded data value
  • "Units": units of the decoded data value
  • "Valid": boolean indicating if the decoded data value is valid (i.e. is within operational data range)

Example JSON object for a fully decoded J1939 CAN message:

{
  "ID": 217056256,
  "Priority": 3,
  "PGN": 61444,
  "PGNName": "Electronic Engine Controller 1",
  "SA": 0,
  "SAName": "Engine #1",
  "DLC": 8,
  "DataRaw": [240, 125, 131, 131, 23, 0, 255, 131],
  "Decoded": true,
  "SPNs": {
    "190": {
      "Name": "Engine Speed",
      "DataRange": "0 to 8,031.875 rpm",
      "OperationalRange": "",
      "OperationalHigh": 8031.875,
      "OperationalLow": 0,
      "StartBit": 24,
      "SPNLength": 16,
      "Resolution": 0.125,
      "Offset": 0,
      "ValueRaw": 6019,
      "ValueDecoded": 752.375,
      "Units": "rpm",
      "Valid": true
    },
    ...
  }
}

The ellipses shows where additional SPN sub-objects would appear.

Important notes:

  • "Decoded" boolean should be checked before using the members in the SPN sub-object(s); if it is false, then the "SPNs" object may not even exist
  • "Valid" boolean from within an SPN sub-object should be checked before using the decoded data value; most often if the decoded data value is not valid, it will be set to the string "Not Available"
  • "PGNName" and "SPNName" values are found using a simple lookup table based on their number values; if the decoded CAN message is not a real J1939 message these members may still exist if the CAN identifier bits happen to match a value found in the J1939 protocol spec although this does not necessarily mean that the CAN message was a valid J1939 message

TODO

  • Improve speed of J1939 DB lookup. At the moment the processing speed to decode a message is not great and at a high message rate it can lag behind if decoding in real-time. The hash-tables branch uses C++ container objects (hash maps) to improve the lookup speed however it is no longer pure C code.