Quantex GmbH
DE RU EN EL
Your region: Europe

J2534 Developer Guide

Last modified:

What is J2534?

J2534 (often referred to as Pass-Thru) is a standard developed by SAE (Society of Automotive Engineers) that solves an important problem: it allows the same diagnostic software to work with diagnostic adapters from different manufacturers.

The main idea:

Imagine you are writing a car diagnostics application. Without the J2534 standard, you would have to write unique code for each adapter (K-Line, CAN adapter, etc.) you want to support. This is complex and expensive.

The J2534 standard defines a unified API (Application Programming Interface) that adapter manufacturers must implement in their drivers. The standard defines this API as a DLL library for Windows only.

For a developer, this means:

Thus, J2534 is a "bridge" between your application and the diagnostic adapter that hides the complexities of specific hardware and allows you to focus on the diagnostics logic.

Many developers, beyond Windows, want to use alternative platforms. In addition to the standard DLL for Windows, we also provide libraries for Linux, macOS, as well as the mobile platforms Android and iOS.


What J2534 does not do (Your area of responsibility)

It is important to understand that J2534 is a standard for the transport layer. It handles all the work of sending and receiving data bytes over the selected physical interface (CAN, K-Line, etc.).

However, the J2534 standard does not define:

This remains the task of your application. As a developer, you need to know which specific bytes to send to request, for example, fault codes or current parameters, and how to parse the response from the control unit.

For this, you will need knowledge of application-level diagnostic protocols, such as:

The J2534 DLL will deliver your bytes to the ECU, but what those bytes are and what to do with the response is determined by your application.


Standards Overview

The J2534 standard is part of a large family of standards governing automotive diagnostics. For a deeper understanding, it is recommended to review the official documents.

Detailed table of protocol and standard relationships

The ScanDoc adapter implements a partial version of the J2534-1 standard and some extensions from J2534-2, and also has its own functions for extending capabilities.


Supported Protocols

The J2534 standard defines a set of physical and transport layer protocols through which data is exchanged with the vehicle's ECU. Each protocol supported by the ScanDoc adapter is described below.

ISO 15765 (CAN with Transport Layer)

The primary protocol for diagnostics of modern vehicles. ISO 15765 (also known as ISO-TP) implements a transport layer on top of the CAN bus: it automatically performs segmentation of long messages into CAN frames, flow control management, and reassembly of responses from multiple frames.

This is the protocol used for diagnostics via UDS (ISO 14229) and OBD-II on the CAN bus. If your task is to send diagnostic requests and receive responses from the ECU, use ISO15765.

// Connect via ISO 15765 at 500 kbit/s
PassThruConnect(deviceID, ISO15765, 0, 500000, &channelID);

CAN (Raw Stream)

The CAN protocol provides access to the raw stream of CAN frames without a transport layer. Each message is a single CAN frame (up to 8 bytes of data). The adapter does not perform segmentation or reassembly — you receive and send frames as-is.

Use this protocol when you need to work with the CAN bus directly: traffic monitoring, sending individual frames, working with non-standard protocols on top of CAN.

// Connect CAN at 500 kbit/s with 29-bit IDs
PassThruConnect(deviceID, CAN, CAN_29BIT_ID, 500000, &channelID);

ISO 14230 (KWP2000)

A K-line diagnostic protocol, widely used in vehicles from the 2000s (especially European). It supports two methods of connection initialization: 5-baud (slow) and fast init. After initialization, communication proceeds at the speed negotiated with the ECU.

Used for dealer-level diagnostics via KWP2000 and for OBD-II on the K-line.

// Connect via ISO 14230 at 10400 baud, K-line only
PassThruConnect(deviceID, ISO14230, ISO9141_K_LINE, 10400, &channelID);

ISO 9141

An earlier K-line protocol, defined by the ISO 9141-2 standard. Used for OBD-II diagnostics in older vehicles (before 2004-2008 depending on region). Supports only 5-baud initialization.

// Connect via ISO 9141 at 10400 baud
PassThruConnect(deviceID, ISO9141, 0, 10400, &channelID);

SAE J1850 VPW

A protocol using Variable Pulse Width modulation, used in General Motors vehicles for OBD-II diagnostics. Operates at 10.4 kbit/s over a single wire.

// Connect via J1850 VPW
PassThruConnect(deviceID, J1850VPW, 0, 10400, &channelID);

SAE J1850 PWM

A protocol using Pulse Width Modulation, used in Ford vehicles for OBD-II diagnostics. Operates at 41.6 kbit/s over two wires.

// Connect via J1850 PWM
PassThruConnect(deviceID, J1850PWM, 0, 41600, &channelID);

Getting Started (Step-by-Step Guide)

Working with a J2534 adapter from your application typically follows this algorithm:

Step 1: Finding and Loading the J2534 DLL

Each J2534 adapter manufacturer provides their own API implementation as a DLL file. When the driver is installed, information about this DLL (file path) is written to the Windows registry.

Your application should:

  1. Find available J2534 devices in the system registry.
  2. Allow the user to choose which adapter to use (if there are multiple).
  3. Load the corresponding DLL into memory using the LoadLibrary function (on Windows).

Step 2: Main Adapter Workflow

After loading the DLL and obtaining function pointers, a typical diagnostic session looks like this:

  1. Open a connection to the adapter:
    Call the PassThruOpen() function to initialize communication with the physical device. In response, you will receive a DeviceID — a unique identifier for this session.
    // Example
    unsigned long deviceID;
    long result = PassThruOpen(NULL, &deviceID);
  2. Establish a communication channel with the ECU:
    Call PassThruConnect() to establish a logical communication channel with the vehicle using a specific protocol (e.g., ISO15765 for the CAN bus). You specify the protocol, speed, and other parameters. In response, you receive a ChannelID.
    // Example
    unsigned long channelID;
    result = PassThruConnect(deviceID, ISO15765, 0, 500000, &channelID);
  3. Set up filters:
    Setting up filters is a mandatory step. By default, the adapter will not receive incoming messages until at least one filter has been configured. Filters allow you to discard all unnecessary traffic on the bus and receive only the data you are interested in (e.g., responses from a specific ECU). The PassThruStartMsgFilter() function is used to create and configure filters.
  4. Data exchange:

    The data exchange process is asynchronous and based on queues:

    • Sending data: Calling PassThruWriteMsg() does not send the message to the bus immediately. Instead, it places one or more messages into the send queue and returns control immediately. The adapter driver will independently send these messages from the queue as soon as possible.
    • Reading data: Messages coming from the bus (e.g., responses from the ECU) accumulate in an internal receive queue. The PassThruReadMsg() function is used to retrieve messages from this queue.

    Important note: The J2534 library is single-threaded. This means that all function calls (PassThruWriteMsg, PassThruReadMsg, etc.) for a single channel must be executed strictly sequentially. You cannot call one function while the previous one is still executing. Attempting to call functions simultaneously from different threads for the same channel will lead to unpredictable behavior.

  5. Close the communication channel:
    After finishing work with the ECU, close the channel using PassThruDisconnect().
    // Example
    result = PassThruDisconnect(channelID);
  6. Close the connection to the adapter:
    At the very end, when work with the adapter is completely finished, call PassThruClose() to release the device.
    // Example
    result = PassThruClose(deviceID);

This cycle is the foundation of any J2534 application. The following sections describe each API function and its parameters in detail.


Error Handling

Every J2534 API function returns a status code. Successful execution always returns STATUS_NOERROR (value 0). Any other value indicates a specific error (e.g., ERR_TIMEOUT, ERR_INVALID_CHANNEL_ID, etc.).

It is extremely important to check the return value after every function call.

A special error code is ERR_FAILED. This is a general, non-specific error. Only in this case should you call PassThruGetLastError() to obtain a more detailed, textual description of the problem from the driver.

// Example of proper error handling
long result = PassThruConnect(deviceID, ISO15765, 0, 500000, &channelID);
if (result != STATUS_NOERROR)
{
    printf("PassThruConnect error. Code: %ld\n", result);

    // If the error is general, request details
    if (result == ERR_FAILED)
    {
        char errorDescription[80];
        PassThruGetLastError(&errorDescription[0], 80);
        printf("  Additional info: %s\n", errorDescription);
    }

    // Here you can add logic for handling other error codes
    // switch (result) { case ERR_TIMEOUT: ... }

    return; // Abort execution
}

J2534 Standard Functions Reference

A detailed description of each J2534 standard function is available in the functions reference.