J1939 Basics and Tools

This is an introductory guide for those looking to work with the J1939 protocol. The page covers the basics of the J1939 protocol and its applications, as well as providing a step-by-step guide on how to work with J1939 data using various tools and methods. The page discusses working with socketCAN in Python, using can-utils, a set of Linux command-line utilities for interfacing with CAN buses, and parsing J1939 data using the pretty_j1939 project, a Python package for decoding and interpreting J1939 messages. The webpage also includes examples and code snippets to help users better understand the concepts and techniques discussed. This page is a great resource for anyone who is new to working with J1939 data and wants to learn more about the protocol and how to use it in their projects.

J1939 Basics

The J1939 protocol is a communication standard used in heavy-duty vehicles and equipment, including commercial trucks, buses, and agricultural equipment. It is based on the Controller Area Network (CAN) protocol and provides a higher level of functionality and flexibility than basic CAN. J1939 allows for the exchange of real-time data between electronic control units (ECUs) and devices in the vehicle or equipment. J1939 is governed by the SAE standards organziation, which defines the protocol and its applications. The protocol is used to communicate with devices on the vehicle's CAN bus, examples include:

  • Engine control and monitoring
  • Transmission control
  • Brake control
  • Vehicle diagnostics

The protocol is also used to communicate with devices that are not directly connected to the vehicle's CAN bus, such as a trailer or a mobile device.

Using can-utils

can-utils is a set of Linux command-line utilities for interfacing with CAN buses. can-utils is a great tool for working with CAN buses and J1939 data. It is easy to use and provides a variety of useful features. This guide will walk you through the basic steps for installing and using can-utils to work with J1939 data. Keep in mind these instructions may need to be modified for your specific system.

Step 1: Install can-utils

can-utils can be installed on Ubuntu and Debian based systems by running the following command:

sudo apt-get install can-utils

Step 2: Find the name of your CAN interface

To find the name of your CAN interface, run the following command:

if addr

The name of the CAN interface is usually can0, but it may be different on your system.

Step 3: Start the can interface

To start the can interface, run the following command (replace 250000 with the correct baudrate for your ECU):

sudo ip link set can0 up baudrate 250000

Step 4: Use can-utils commands

Now that the can interface is up and running, you can use various can-utils commands to interact with the CAN bus. Some useful commands include:

  • cangen - generates test CAN messages
  • candump - listens to and logs CAN messages
  • cansend - sends a single CAN message

Example: Sending a J1939 message with cansend

To send a J1939 message using cansend, use the following command:

cansend can0 18DAF123#DEADBEEFDABBAD00

This will send a J1939 message with an arbitration ID of 0x18DAF123 and data bytes [0xDE, 0xAD, 0xBE, 0xEF, 0xDA, 0xBB, 0xAD, 0x00] on the can0 interface.

Using socketCAN in Python

What if we wanted to not only read, but also process the data in real time? This would be important to respond to requests or commands that are coming from the CAN bus. To do this, we'll need to write a program to process and read the data. This is where socketCAN comes in. socketCAN is a Linux kernel module that provides an interface for working with CAN buses. It is a great tool for working with CAN buses and J1939 data. It is easy to use and provides a variety of useful features. This guide will walk you through the basic steps for installing and using socketCAN to work with J1939 data. Keep in mind these instructions may need to be modified for your specific system.

Unfortunately, this code does not yet display properly on smaller screens.

1#!/usr/bin/python3
2
3import time
4import socket
5import struct
6import sys
7
8# Open a socket and bind to it from SocketCAN
9sock = socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
10# Allows socket to receive error frames
11sock.setsockopt(socket.SOL_CAN_RAW, socket.CAN_RAW_ERR_FILTER, socket.CAN_ERR_MASK) 
12# The interface can be seen from the command prompt (ifconfig)
13# The can channel must be configured using the ip link commands
14interface = "can1"
15
16# Bind to the interface
17try:
18    sock.bind((interface,))
19except OSError:
20    print("Could not bind to interface '%s'
21" % interface)
22    sys.exit()
23
24# The basic CAN frame structure and the sockaddr structure are defined
25#   in include/linux/can.h:
26#     struct can_frame {
27#             canid_t can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */
28#             __u8    can_dlc; /* frame payload length in byte (0 .. 8) */
29#             __u8    __pad;   /* padding */
30#             __u8    __res0;  /* reserved / padding */
31#             __u8    __res1;  /* reserved / padding */
32#             __u8    data[8] __attribute__((aligned(8)));
33#     };   
34
35# To match this data structure, the following struct format can be used:
36can_frame_format = "<LB3x8s"
37# Unsigned Long Integer (little endian), unsigned char (byte), three pad bytes, eight chars (bytes)
38# Note: this is 16 bytes
39
40
41# These are defined in can.h of the Linux kernel sources
42# e.g. https://github.com/torvalds/linux/blob/master/include/uapi/linux/can.h
43print("socket.CAN_EFF_MASK = 0x{:08X}".format(socket.CAN_EFF_MASK))
44print("socket.CAN_EFF_FLAG = 0x{:08X}".format(socket.CAN_EFF_FLAG))
45print("socket.CAN_RTR_FLAG = 0x{:08X}".format(socket.CAN_RTR_FLAG))
46print("socket.CAN_ERR_FLAG = 0x{:08X}".format(socket.CAN_ERR_FLAG))
47
48# Enter a loop to read and display the data
49while True:
50    can_packet = sock.recv(16) 
51    can_id, can_dlc, can_data = struct.unpack(can_frame_format, can_packet)
52
53    extended_frame = bool(can_id & socket.CAN_EFF_FLAG)
54    error_frame = bool(can_id & socket.CAN_ERR_FLAG)
55    remote_tx_req_frame = bool(can_id & socket.CAN_RTR_FLAG)
56    
57    if error_frame:
58        can_id &= socket.CAN_ERR_MASK
59        can_id_string = "{:08X} (ERROR)".format(can_id)
60    else: #Data Frame
61        if extended_frame:
62            can_id &= socket.CAN_EFF_MASK
63            can_id_string = "{:08X}".format(can_id)
64        else: #Standard Frame
65            can_id &= socket.CAN_SFF_MASK
66            can_id_string = "{:03X}".format(can_id)
67        
68    if remote_tx_req_frame:
69        can_id_string = "{:08X} (RTR)".format(can_id)
70    
71    hex_data_print = ' '.join(["{:02X}".format(can_byte) for can_byte in can_data])
72    print("{:12.6f} {} [{}] {}".format(time.time(), can_id_string, can_dlc, hex_data_print))

Let's assume we name the above script as 'candump_example.py'. Then it can be run at the command prompt as

python3 candump_example.py.

The output to both the screen and the file will match the candump format from can-utils. Since it is in a while True loop, press ctrl-C to stop. This simple script only works for one interface. If we want more interfaces, then it makes sense to convert this to a class and instantiate a python object for each interface that's available. This class can be threaded, and part of a bigger program to work with the CAN data.

Pretty J1939

A very useful tool to decode the CAN message is the pretty_j1939 project. We take advantage of this database in json form when we work through the examples in the TruckCape Repository. In fact, this repository has all the solutions needed to work with the heavy vehicle CAN systems.