In 32 bit Python.
The Anonymous Automotive Alliance Presents
A J2534 Implementation.
In 32 bit Python.
Deployed 21/10/2025
Communication is Essential.
Without it what would we have?
A comprehensive Python implementation of the SAE J2534 PassThru API for automotive diagnostics and ECU programming.
- Overview
- Features
- Requirements
- Installation
- Quick Start
- Architecture
- API Reference
- Usage Examples
- Error Handling
- Troubleshooting
- Advanced Topics
- License
The J2534 Python Library provides a complete Python interface to SAE J2534-compliant PassThru devices. J2534 is a standard API that allows software applications to communicate with vehicle Electronic Control Units (ECUs) through a PassThru device.
This library is a direct Python port from C#, maintaining exact naming conventions and structure for compatibility and ease of migration.
J2534 (SAE J2534) is a standard that defines an interface between a computer application and a vehicle communication interface device. It enables:
- Vehicle Diagnostics: Read and clear diagnostic trouble codes (DTCs)
- ECU Programming: Flash and reprogram vehicle control modules
- Data Logging: Monitor real-time vehicle data
- Network Analysis: Analyze CAN, ISO-TP, and other automotive protocols
- âś… Complete J2534 API Implementation: Full support for all J2534 v04.04 and v05.00 functions
- âś… Multiple Protocol Support: CAN, ISO15765, ISO9141, ISO14230, J1850 VPW/PWM, and more
- âś… Extended Functions: Battery voltage reading, buffer management, configuration
- âś… Type Safety: Extensive use of Python enums and type hints
- âś… Cross-Platform: Works on Windows (with native J2534 DLLs) and can be extended for Linux
- âś… Memory Management: Proper ctypes memory handling for safe native interop
- âś… Exact C# Compatibility: Preserved naming conventions for easy migration
- Python 3.7 or higher
- Windows OS (for native J2534 DLL support)
- A J2534-compliant PassThru device and its driver DLL
ctypes(standard library)enum(standard library)typing(standard library)
No external dependencies required!
- Copy the J2534.py file to your project directory:
cp J2534.py /path/to/your/project/- Import in your Python code:
from J2534 import *- Ensure your J2534 device drivers are installed on your system.
from J2534 import *
# Create a J2534 device instance
device = J2534Device()
device.Name = "My PassThru Device"
device.FunctionLibrary = "C:\\Path\\To\\J2534Device.dll"
# Create J2534 functions wrapper
j2534 = J2534Functions()
# Load the device library
if j2534.LoadLibrary(device):
print("Library loaded successfully!")
# Open device
deviceId = c_uint(0)
result = j2534.PassThruOpen(c_void_p(0), deviceId)
if result == J2534Err.STATUS_NOERROR:
print(f"Device opened with ID: {deviceId.value}")
# Connect to CAN bus
channelId = c_uint(0)
result = j2534.PassThruConnect(
deviceId.value,
ProtocolID.ISO15765,
ConnectFlag.NONE,
BaudRate.ISO15765_500000,
channelId
)
if result == J2534Err.STATUS_NOERROR:
print(f"Connected to bus with channel ID: {channelId.value}")
# Your diagnostic operations here...
# Disconnect when done
j2534.PassThruDisconnect(channelId.value)
# Close device
j2534.PassThruClose(deviceId.value)
# Free library
j2534.FreeLibrary()
else:
print("Failed to load library!")# Create a message
msg = PassThruMsg(
ProtocolID.ISO15765,
TxFlag.NONE,
bytes([0x00, 0x00, 0x07, 0xE0, 0x01, 0x00]) # Example OBD-II request
)
# Send the message
msgPtr = Utils.ToIntPtr(msg)
numMsgs = c_int(1)
timeout = 1000 # ms
result = j2534.PassThruWriteMsgs(
channelId.value,
msgPtr,
numMsgs,
timeout
)
if result == J2534Err.STATUS_NOERROR:
print(f"Message sent successfully! {numMsgs.value} messages transmitted.")# Allocate memory for received messages
numMsgs = c_int(10)
rxMsgs = cast(create_string_buffer(sizeof(PassThruMsg) * numMsgs.value), c_void_p)
timeout = 1000 # ms
result = j2534.PassThruReadMsgs(
channelId.value,
rxMsgs,
numMsgs,
timeout
)
if result == J2534Err.STATUS_NOERROR:
messages = Utils.AsMsgList(rxMsgs, numMsgs.value)
for msg in messages:
print(f"Received message: {msg.GetBytes().hex()}")J2534.py
├── Enumerations
│ ├── ConfigParameter # Configuration parameters
│ ├── ProtocolID # Communication protocols
│ ├── BaudRate # Baud rate definitions
│ ├── J2534Err # Error codes
│ ├── TxFlag # Transmission flags
│ ├── RxStatus # Reception status flags
│ ├── ConnectFlag # Connection flags
│ ├── FilterType # Message filter types
│ ├── PinNumber # Pin numbering
│ ├── PinVoltage # Voltage levels
│ ├── Ioctl # IOCTL commands
│ └── GET_DEVICE_INFO_Defines
│
├── Interfaces
│ ├── IJ2534 # Base J2534 interface
│ └── IJ2534Extended # Extended functionality
│
├── Core Classes
│ ├── J2534Device # Device information
│ ├── J2534Functions # Standard J2534 functions
│ ├── J2534FunctionsExtended # Extended functions
│ ├── J2534DllWrapper # DLL loading and function pointers
│ └── J2534Exception # Exception handling
│
├── Structures
│ ├── PassThruMsg # Message structure
│ ├── SConfig # Configuration structure
│ ├── SConfigList # Configuration list
│ ├── SParam # Parameter structure
│ ├── SParamList # Parameter list
│ ├── SByteArray # Byte array structure
│ ├── SDEVICE # Device structure
│
└── Utilities
├── NativeMethods # Native DLL interaction
└── Utils # Helper functions
Represents a J2534 PassThru device.
class J2534Device:
def __init__(self):
self.Vendor = "" # Device vendor name
self.Name = "" # Device name
self.FunctionLibrary = "" # Path to device DLL
self.ConfigApplication = "" # Configuration application path
# Protocol support flags
self.CAN = 0
self.ISO14230 = 0
self.ISO15765 = 0
self.ISO9141 = 0
self.J1850PWM = 0
self.J1850VPW = 0
# ... additional protocol flagsProperties:
Vendor(str): Manufacturer nameName(str): Device model nameFunctionLibrary(str): Full path to the J2534 DLL- Protocol support properties indicate which protocols are supported (0 = not supported, >0 = supported)
Main class for J2534 API operations.
class J2534Functions(IJ2534):
def LoadLibrary(self, device: J2534Device) -> bool
def FreeLibrary(self) -> bool
def PassThruOpen(self, name: c_void_p, deviceId: c_uint) -> J2534Err
def PassThruClose(self, deviceId: c_uint) -> J2534Err
def PassThruConnect(self, deviceId: c_uint, protocolId: ProtocolID,
flags: ConnectFlag, baudRate: BaudRate,
channelId: c_uint) -> J2534Err
def PassThruDisconnect(self, channelId: c_int) -> J2534Err
def PassThruReadMsgs(self, channelId: c_int, msgs: c_void_p,
numMsgs: c_int, timeout: c_int) -> J2534Err
def PassThruWriteMsgs(self, channelId: c_int, msgs: c_void_p,
numMsgs: c_int, timeout: c_int) -> J2534Err
def PassThruStartPeriodicMsg(self, channelId: c_int, msg: c_void_p,
msgId: c_int, timeInterval: c_int) -> J2534Err
def PassThruStopPeriodicMsg(self, channelId: c_int, msgId: c_int) -> J2534Err
def PassThruStartMsgFilter(self, channelid: c_int, filterType: FilterType,
maskMsg: c_void_p, patternMsg: c_void_p,
flowControlMsg: c_void_p, filterId: c_int) -> J2534Err
def PassThruStopMsgFilter(self, channelId: c_int, filterId: c_int) -> J2534Err
def PassThruSetProgrammingVoltage(self, deviceId: c_int,
pinNumber: PinNumber,
voltage: c_uint) -> J2534Err
def PassThruReadVersion(self, deviceId: c_int, firmwareVersion: c_void_p,
dllVersion: c_void_p, apiVersion: c_void_p) -> J2534Err
def PassThruGetLastError(self, errorDescription: c_void_p) -> J2534Err
def PassThruIoctl(self, channelId: c_int, ioctlID: c_int,
input: c_void_p, output: c_void_p) -> J2534ErrKey Methods:
Loads the J2534 DLL specified in the device configuration.
Parameters:
device: J2534Device object with FunctionLibrary path set
Returns:
Trueif library loaded successfullyFalseif loading failed
Opens a communication connection to the PassThru device.
Parameters:
name: Reserved for future use (passc_void_p(0))deviceId: Reference to receive the device ID
Returns:
J2534Err.STATUS_NOERRORon success- Error code on failure
Establishes a logical communication channel using a specific protocol.
Parameters:
deviceId: Device ID from PassThruOpenprotocolId: Communication protocol (ProtocolID enum)flags: Connection flags (ConnectFlag enum)baudRate: Communication speed (BaudRate enum)channelId: Reference to receive the channel ID
Returns:
J2534Err.STATUS_NOERRORon success- Error code on failure
Transmits messages on the specified channel.
Parameters:
channelId: Channel ID from PassThruConnectmsgs: Pointer to PassThruMsg arraynumMsgs: Reference to number of messages (input/output)timeout: Timeout in milliseconds
Returns:
J2534Err.STATUS_NOERRORon success- Error code on failure
Receives messages from the specified channel.
Parameters:
channelId: Channel ID from PassThruConnectmsgs: Pointer to receive message arraynumMsgs: Reference to number of messages (input/output)timeout: Timeout in milliseconds
Returns:
J2534Err.STATUS_NOERRORon successJ2534Err.ERR_BUFFER_EMPTYif no messages available- Other error codes on failure
Extended functionality beyond the standard J2534 API.
class J2534FunctionsExtended(J2534Functions, IJ2534Extended):
def GetConfig(self, channelId: c_int, config: List[SConfig]) -> J2534Err
def SetConfig(self, channelId: c_int, config: List[SConfig]) -> J2534Err
def ReadBatteryVoltage(self, deviceId: c_int, voltage: c_int) -> J2534Err
def ReadProgrammingVoltage(self, deviceId: c_int, voltage: c_int) -> J2534Err
def SW_CAN_BusSpeed(self, ChannelID: c_int, Option: c_int) -> J2534Err
def FiveBaudInit(self, channelId: c_int, targetAddress: c_byte,
keyword1: c_byte, keyword2: c_byte) -> J2534Err
def FastInit(self, channelId: c_int, txMsg: PassThruMsg,
rxMsg: PassThruMsg) -> J2534Err
def ClearTxBuffer(self, channelId: c_int) -> J2534Err
def ClearRxBuffer(self, channelId: c_int) -> J2534Err
def ClearPeriodicMsgs(self, channelId: c_int) -> J2534Err
def ClearMsgFilters(self, channelId: c_int) -> J2534Err
def ReadAllMessages(self, channelId: c_int, numMsgs: c_int,
timeout: c_int, messages: List[PassThruMsg],
readUntilTimeout: bool = True) -> J2534ErrExtended Methods:
Retrieves current configuration parameters.
Example:
config = [
SConfig(ConfigParameter.DATA_RATE, 0),
SConfig(ConfigParameter.LOOPBACK, 0)
]
result = j2534_ext.GetConfig(channelId.value, config)
if result == J2534Err.STATUS_NOERROR:
print(f"Data Rate: {config[0].Value}")
print(f"Loopback: {config[1].Value}")Sets configuration parameters.
Example:
config = [
SConfig(ConfigParameter.LOOPBACK, 1), # Enable loopback
SConfig(ConfigParameter.DATA_RATE, 500000) # Set 500k baud
]
result = j2534_ext.SetConfig(channelId.value, config)Reads the vehicle battery voltage.
Example:
voltage = c_int(0)
result = j2534_ext.ReadBatteryVoltage(deviceId.value, voltage)
if result == J2534Err.STATUS_NOERROR:
voltage_mv = voltage.value
voltage_v = voltage_mv / 1000.0
print(f"Battery Voltage: {voltage_v:.2f}V")Continuously reads messages until timeout or buffer empty.
Parameters:
channelId: Channel IDnumMsgs: Maximum messages to read per calltimeout: Timeout in millisecondsmessages: List to populate with received messagesreadUntilTimeout: If True, read until timeout; if False, read one batch
Example:
messages = []
result = j2534_ext.ReadAllMessages(
channelId.value,
10, # Read up to 10 messages at a time
1000, # 1 second timeout
messages,
readUntilTimeout=True
)
for msg in messages:
print(f"Received: {msg.GetBytes().hex()}")Defines supported communication protocols.
class ProtocolID(IntEnum):
J1850VPW = 0x01 # J1850 VPW (GM)
J1850PWM = 0x02 # J1850 PWM (Ford)
ISO9141 = 0x03 # ISO 9141
ISO14230 = 0x04 # ISO 14230 (KWP2000)
CAN = 0x05 # Raw CAN
ISO15765 = 0x06 # ISO 15765 (ISO-TP/UDS)
SCI_A_ENGINE = 0x07 # Chrysler SCI (Engine)
SCI_A_TRANS = 0x08 # Chrysler SCI (Transmission)
SCI_B_ENGINE = 0x09 # Chrysler SCI (Engine)
SCI_B_TRANS = 0x0A # Chrysler SCI (Transmission)
ISO15765_LOGICAL = 0x200 # ISO 15765 Logical
# J2534-2 protocols
J1850VPW_PS = 0x8000 # J1850 VPW Passive
J1850PWM_PS = 0x8001 # J1850 PWM Passive
ISO9141_PS = 0x8002 # ISO 9141 Passive
ISO14230_PS = 0x8003 # ISO 14230 Passive
CAN_PS = 0x8004 # CAN Passive
ISO15765_PS = 0x8005 # ISO 15765 Passive
# ... additional protocolsError codes returned by J2534 functions.
class J2534Err(IntEnum):
STATUS_NOERROR = 0x00 # Success
ERR_NOT_SUPPORTED = 0x01 # Function not supported
ERR_INVALID_CHANNEL_ID = 0x02 # Invalid channel ID
ERR_INVALID_PROTOCOL_ID = 0x03 # Invalid protocol ID
ERR_NULL_PARAMETER = 0x04 # NULL pointer supplied
ERR_INVALID_FLAGS = 0x06 # Invalid flags
ERR_FAILED = 0x07 # Undefined error
ERR_ConnectedDevice_NOT_CONNECTED = 0x08 # Device not connected
ERR_TIMEOUT = 0x09 # Timeout
ERR_INVALID_MSG = 0x0A # Invalid message
ERR_INVALID_TIME_INTERVAL = 0x0B # Invalid time interval
ERR_EXCEEDED_LIMIT = 0x0C # Exceeded limit
ERR_INVALID_MSG_ID = 0x0D # Invalid message ID
ERR_ConnectedDevice_IN_USE = 0x0E # Device in use
ERR_INVALID_IOCTL_ID = 0x0F # Invalid IOCTL ID
ERR_BUFFER_EMPTY = 0x10 # Buffer empty
ERR_BUFFER_FULL = 0x11 # Buffer full
ERR_BUFFER_OVERFLOW = 0x12 # Buffer overflow
ERR_PIN_INVALID = 0x13 # Invalid pin number
ERR_CHANNEL_IN_USE = 0x14 # Channel in use
ERR_MSG_PROTOCOL_ID = 0x15 # Protocol mismatch
ERR_INVALID_FILTER_ID = 0x16 # Invalid filter ID
ERR_NO_FLOW_CONTROL = 0x17 # No flow control filter
ERR_NOT_UNIQUE = 0x18 # Not unique
ERR_INVALID_BAUDRATE = 0x19 # Invalid baud rate
ERR_INVALID_ConnectedDevice_ID = 0x1A # Invalid device ID
# ... additional error codesCommon baud rate values for different protocols.
class BaudRate(IntEnum):
ISO9141_10400 = 10400
ISO9141_10000 = 10000
ISO14230_10400 = 10400
ISO14230_10000 = 10000
J1850PWM_41600 = 41600
J1850PWM_83300 = 83300
J1850VPW_10400 = 10400
J1850VPW_41600 = 41600
CAN_125000 = 125000
CAN_250000 = 250000
CAN_500000 = 500000
CAN_33333 = 33333
CAN_83333 = 83333
ISO15765_125000 = 125000
ISO15765_250000 = 250000
ISO15765_500000 = 500000
GMUART_8192 = 8192
GMUART_160 = 160Transmission flags for PassThruMsg.
class TxFlag(Flag):
NONE = 0x00000000
SCI_TX_VOLTAGE = 0x00800000 # SCI high voltage (12V)
SCI_MODE = 0x00400000 # SCI mode
WAIT_P3_MIN_ONLY = 0x00000200 # Wait P3_MIN only
CAN_29BIT_ID = 0x00000100 # Use 29-bit CAN identifier
ISO15765_ADDR_TYPE = 0x00000080 # ISO15765 addressing type
ISO15765_FRAME_PAD = 0x00000040 # Pad ISO15765 frame
SW_CAN_HV_TX = 0x00000400 # SW-CAN high voltage TXReception status flags for PassThruMsg.
class RxStatus(Flag):
NONE = 0x00000000
TX_MSG_TYPE = 0x00000001 # Message is from TX
START_OF_MESSAGE = 0x00000002 # First message
RX_BREAK = 0x00000004 # Break received
TX_INDICATION_SUCCESS = 0x00000008 # TX successful
ISO15765_PADDING_ERROR = 0x00000010 # Padding error
ERROR_INDICATION = 0x20 # Error occurred
BUFFER_OVERFLOW = 0x40 # Buffer overflow
ISO15765_ADDR_TYPE = 0x00000080 # ISO15765 addressing
CAN_29BIT_ID = 0x00000100 # 29-bit CAN ID
TX_FAILED = 0x200 # TX failed
SW_CAN_HV_TX = 0x00000400 # SW-CAN HV TX
SW_CAN_NS_RX = 0x00040000 # SW-CAN normal speed RX
SW_CAN_HS_RX = 0x00020000 # SW-CAN high speed RX
SW_CAN_HV_RX = 0x00010000 # SW-CAN high voltage RXConnection flags for PassThruConnect.
class ConnectFlag(Flag):
NONE = 0x0000
ISO9141_K_LINE_ONLY = 0x1000 # Use K-line only (no L-line)
CAN_ID_BOTH = 0x0800 # 11-bit and 29-bit CAN
ISO9141_NO_CHECKSUM = 0x0200 # No checksum
CAN_29BIT_ID = 0x0100 # Use 29-bit CAN IDs
FULL_DUPLEX = 0x1 # Full duplex modeConfiguration parameters for GetConfig/SetConfig.
class ConfigParameter(IntEnum):
DATA_RATE = 0x01 # Baud rate value
LOOPBACK = 0x03 # Loopback mode (0=off, 1=on)
NODE_ADDRESS = 0x04 # Node address for J1850
NETWORK_LINE = 0x05 # Network line (0=BUS_NORMAL, 1=BUS_PLUS, 2=BUS_MINUS)
P1_MIN = 0x06 # ISO 9141 P1 min timing
P1_MAX = 0x07 # ISO 9141 P1 max timing
P2_MIN = 0x08 # ISO 9141 P2 min timing
P2_MAX = 0x09 # ISO 9141 P2 max timing
P3_MIN = 0x0A # ISO 9141 P3 min timing
P3_MAX = 0x0B # ISO 9141 P3 max timing
P4_MIN = 0x0C # ISO 9141 P4 min timing
P4_MAX = 0x0D # ISO 9141 P4 max timing
W1 = 0x0E # ISO 9141 W1 timing
W2 = 0x0F # ISO 9141 W2 timing
W3 = 0x10 # ISO 9141 W3 timing
W4 = 0x11 # ISO 9141 W4 timing
W5 = 0x12 # ISO 9141 W5 timing
TIDLE = 0x13 # Bus idle time
TINIL = 0x14 # Initialization time
TWUP = 0x15 # Wakeup time
PARITY = 0x16 # Parity (0=no parity, 1=odd, 2=even)
BIT_SAMPLE_POINT = 0x17 # CAN bit sample point (%)
SYNC_JUMP_WIDTH = 0x18 # CAN sync jump width
# ISO15765 parameters
ISO15765_BS = 0x1E # Block size
ISO15765_STMIN = 0x1F # Separation time minimum
ISO15765_WFT_MAX = 0x25 # Wait frame transmit maximum
ISO15765_PAD_VALUE = 0X2B # Padding value
# ... many more parameters availableMain message structure for J2534 communication.
class PassThruMsg(Structure):
_fields_ = [
("ProtocolID", c_int), # Protocol identifier
("RxStatus", c_uint), # Reception status flags
("TxFlags", c_uint), # Transmission flags
("Timestamp", c_uint), # Message timestamp (microseconds)
("DataSize", c_uint), # Number of data bytes
("ExtraDataIndex", c_uint), # Start of extra data
("Data", c_byte * 4128) # Message data (max 4128 bytes)
]
def __init__(self, myProtocolId: ProtocolID = None,
myTxFlag: TxFlag = None,
myByteArray: bytes = None)
def SetBytes(self, myByteArray: bytes)
def GetBytes(self) -> bytes
def ToString(self, tab: str) -> strUsage Example:
# Create a CAN message
msg = PassThruMsg(
ProtocolID.CAN,
TxFlag.CAN_29BIT_ID,
bytes([0x18, 0xDA, 0xF1, 0x10, 0x02, 0x01, 0x00])
)
# Access message properties
print(f"Protocol: {msg.ProtocolID}")
print(f"Data Size: {msg.DataSize}")
print(f"Data: {msg.GetBytes().hex()}")
# Modify message data
msg.SetBytes(bytes([0x18, 0xDA, 0xF1, 0x10, 0x02, 0x01, 0x01]))Configuration parameter structure.
class SConfig(Structure):
_fields_ = [
("Parameter", c_int), # ConfigParameter enum value
("Value", c_uint16) # Parameter value
]Usage Example:
# Create configuration
config = SConfig()
config.Parameter = ConfigParameter.DATA_RATE
config.Value = 500000
# Or use constructor
config = SConfig(ConfigParameter.LOOPBACK, 1)Byte array structure for IOCTL operations.
class SByteArray(Structure):
_fields_ = [
("NumOfBytes", c_int), # Number of bytes
("BytePtr", c_void_p) # Pointer to byte array
]Helper methods for working with J2534 structures.
class Utils:
@staticmethod
def AsString(ptr: c_void_p) -> str
"""Convert IntPtr to string"""
@staticmethod
def ToIntPtr(msg: PassThruMsg) -> c_void_p
"""Convert PassThruMsg to IntPtr"""
@staticmethod
def AsMsgList(ptr: c_void_p, count: int) -> List[PassThruMsg]
"""Convert IntPtr to PassThruMsg list"""
@staticmethod
def AsList(ptr: c_void_p, count: int, struct_type: type) -> List
"""Convert IntPtr to list of structures"""
@staticmethod
def AsStruct(ptr: c_void_p, struct_type: type)
"""Convert IntPtr to structure"""
@staticmethod
def AsNullableStruct(ptr: c_void_p, struct_type: type) -> Optional
"""Convert IntPtr to nullable structure"""from J2534 import *
def read_vin():
# Setup
device = J2534Device()
device.FunctionLibrary = "C:\\Program Files\\...\\J2534.dll"
j2534 = J2534Functions()
if not j2534.LoadLibrary(device):
print("Failed to load library")
return
# Open device
deviceId = c_uint(0)
result = j2534.PassThruOpen(c_void_p(0), deviceId)
if result != J2534Err.STATUS_NOERROR:
print(f"Failed to open device: {result}")
return
# Connect to ISO15765 (CAN)
channelId = c_uint(0)
result = j2534.PassThruConnect(
deviceId.value,
ProtocolID.ISO15765,
ConnectFlag.NONE,
BaudRate.ISO15765_500000,
channelId
)
if result != J2534Err.STATUS_NOERROR:
print(f"Failed to connect: {result}")
j2534.PassThruClose(deviceId.value)
return
try:
# Set up flow control filter (required for ISO15765)
maskMsg = PassThruMsg(ProtocolID.ISO15765, TxFlag.NONE,
bytes([0x00, 0x00, 0x00, 0x00]))
patternMsg = PassThruMsg(ProtocolID.ISO15765, TxFlag.NONE,
bytes([0x00, 0x00, 0x07, 0xE8]))
flowMsg = PassThruMsg(ProtocolID.ISO15765, TxFlag.NONE,
bytes([0x00, 0x00, 0x07, 0xE0]))
filterId = c_int(0)
result = j2534.PassThruStartMsgFilter(
channelId.value,
FilterType.FLOW_CONTROL_FILTER,
Utils.ToIntPtr(maskMsg),
Utils.ToIntPtr(patternMsg),
Utils.ToIntPtr(flowMsg),
filterId
)
if result != J2534Err.STATUS_NOERROR:
print(f"Failed to start filter: {result}")
return
# Send VIN request (Service 09, PID 02)
vinRequest = PassThruMsg(
ProtocolID.ISO15765,
TxFlag.NONE,
bytes([0x00, 0x00, 0x07, 0xDF, 0x09, 0x02])
)
txMsgPtr = Utils.ToIntPtr(vinRequest)
numMsgs = c_int(1)
result = j2534.PassThruWriteMsgs(
channelId.value,
txMsgPtr,
numMsgs,
1000
)
if result != J2534Err.STATUS_NOERROR:
print(f"Failed to send request: {result}")
return
# Read response
numMsgs = c_int(10)
rxMsgs = cast(create_string_buffer(sizeof(PassThruMsg) * numMsgs.value), c_void_p)
result = j2534.PassThruReadMsgs(
channelId.value,
rxMsgs,
numMsgs,
2000
)
if result == J2534Err.STATUS_NOERROR:
messages = Utils.AsMsgList(rxMsgs, numMsgs.value)
for msg in messages:
data = msg.GetBytes()
# Skip addressing bytes, extract VIN
if len(data) > 5 and data[4] == 0x49 and data[5] == 0x02:
vin_bytes = data[6:]
vin = vin_bytes.decode('ascii', errors='ignore')
print(f"VIN: {vin}")
# Cleanup filter
j2534.PassThruStopMsgFilter(channelId.value, filterId.value)
finally:
# Disconnect and close
j2534.PassThruDisconnect(channelId.value)
j2534.PassThruClose(deviceId.value)
j2534.FreeLibrary()
if __name__ == "__main__":
read_vin()from J2534 import *
import time
def monitor_can_bus(duration_seconds=10):
device = J2534Device()
device.FunctionLibrary = "C:\\Path\\To\\J2534.dll"
j2534_ext = J2534FunctionsExtended()
if not j2534_ext.LoadLibrary(device):
print("Failed to load library")
return
deviceId = c_uint(0)
result = j2534_ext.PassThruOpen(c_void_p(0), deviceId)
if result != J2534Err.STATUS_NOERROR:
print(f"Failed to open: {result}")
return
# Connect in passive mode (sniff mode)
channelId = c_uint(0)
result = j2534_ext.PassThruConnect(
deviceId.value,
ProtocolID.CAN,
ConnectFlag.NONE, # Passive listening
BaudRate.CAN_500000,
channelId
)
if result != J2534Err.STATUS_NOERROR:
print(f"Failed to connect: {result}")
j2534_ext.PassThruClose(deviceId.value)
return
try:
# Set up pass-all filter to receive everything
maskMsg = PassThruMsg(ProtocolID.CAN, TxFlag.NONE,
bytes([0x00, 0x00, 0x00, 0x00]))
patternMsg = PassThruMsg(ProtocolID.CAN, TxFlag.NONE,
bytes([0x00, 0x00, 0x00, 0x00]))
filterId = c_int(0)
result = j2534_ext.PassThruStartMsgFilter(
channelId.value,
FilterType.PASS_FILTER,
Utils.ToIntPtr(maskMsg),
Utils.ToIntPtr(patternMsg),
c_void_p(0),
filterId
)
print(f"Monitoring CAN bus for {duration_seconds} seconds...")
print("CAN ID Data")
print("-" * 50)
start_time = time.time()
message_count = 0
while time.time() - start_time < duration_seconds:
messages = []
result = j2534_ext.ReadAllMessages(
channelId.value,
50, # Read up to 50 messages
100, # 100ms timeout
messages,
readUntilTimeout=False # Single batch
)
for msg in messages:
data = msg.GetBytes()
can_id = int.from_bytes(data[0:4], 'big') & 0x1FFFFFFF
payload = data[4:]
print(f"0x{can_id:08X} {payload.hex().upper()}")
message_count += 1
print(f"\nReceived {message_count} messages")
j2534_ext.PassThruStopMsgFilter(channelId.value, filterId.value)
finally:
j2534_ext.PassThruDisconnect(channelId.value)
j2534_ext.PassThruClose(deviceId.value)
j2534_ext.FreeLibrary()
if __name__ == "__main__":
monitor_can_bus(duration_seconds=30)from J2534 import *
def read_dtcs():
"""Read DTCs using UDS (ISO 14229) over ISO15765"""
device = J2534Device()
device.FunctionLibrary = "C:\\Path\\To\\J2534.dll"
j2534 = J2534Functions()
if not j2534.LoadLibrary(device):
print("Failed to load library")
return
deviceId = c_uint(0)
result = j2534.PassThruOpen(c_void_p(0), deviceId)
if result != J2534Err.STATUS_NOERROR:
return
channelId = c_uint(0)
result = j2534.PassThruConnect(
deviceId.value,
ProtocolID.ISO15765,
ConnectFlag.NONE,
BaudRate.ISO15765_500000,
channelId
)
if result != J2534Err.STATUS_NOERROR:
j2534.PassThruClose(deviceId.value)
return
try:
# Set up filter
maskMsg = PassThruMsg(ProtocolID.ISO15765, TxFlag.NONE,
bytes([0x00, 0x00, 0x00, 0x00]))
patternMsg = PassThruMsg(ProtocolID.ISO15765, TxFlag.NONE,
bytes([0x00, 0x00, 0x07, 0xE8]))
flowMsg = PassThruMsg(ProtocolID.ISO15765, TxFlag.NONE,
bytes([0x00, 0x00, 0x07, 0xE0]))
filterId = c_int(0)
j2534.PassThruStartMsgFilter(
channelId.value,
FilterType.FLOW_CONTROL_FILTER,
Utils.ToIntPtr(maskMsg),
Utils.ToIntPtr(patternMsg),
Utils.ToIntPtr(flowMsg),
filterId
)
# Send Read DTC request (Service 0x19, Sub-function 0x02)
dtcRequest = PassThruMsg(
ProtocolID.ISO15765,
TxFlag.NONE,
bytes([0x00, 0x00, 0x07, 0xE0, 0x19, 0x02, 0x0F]) # Read DTCs by status mask
)
txMsgPtr = Utils.ToIntPtr(dtcRequest)
numMsgs = c_int(1)
j2534.PassThruWriteMsgs(channelId.value, txMsgPtr, numMsgs, 1000)
# Read response
numMsgs = c_int(20)
rxMsgs = cast(create_string_buffer(sizeof(PassThruMsg) * numMsgs.value), c_void_p)
result = j2534.PassThruReadMsgs(channelId.value, rxMsgs, numMsgs, 2000)
if result == J2534Err.STATUS_NOERROR:
messages = Utils.AsMsgList(rxMsgs, numMsgs.value)
for msg in messages:
data = msg.GetBytes()
# Parse DTC response (0x59 is positive response to 0x19)
if len(data) > 5 and data[4] == 0x59 and data[5] == 0x02:
dtc_count = data[6]
print(f"Found {dtc_count} DTCs:")
# Parse DTCs (3 bytes each)
idx = 7
for i in range(dtc_count):
if idx + 2 < len(data):
dtc = (data[idx] << 16) | (data[idx+1] << 8) | data[idx+2]
# Convert to standard DTC format (P0xxx, C0xxx, etc.)
first_char = ['P', 'C', 'B', 'U'][(dtc >> 14) & 0x3]
second_digit = (dtc >> 12) & 0x3
remaining = dtc & 0xFFF
dtc_string = f"{first_char}{second_digit}{remaining:03X}"
print(f" DTC: {dtc_string}")
idx += 3
j2534.PassThruStopMsgFilter(channelId.value, filterId.value)
finally:
j2534.PassThruDisconnect(channelId.value)
j2534.PassThruClose(deviceId.value)
j2534.FreeLibrary()
if __name__ == "__main__":
read_dtcs()from J2534 import *
def clear_dtcs():
"""Clear DTCs using UDS service 0x14"""
device = J2534Device()
device.FunctionLibrary = "C:\\Path\\To\\J2534.dll"
j2534 = J2534Functions()
if not j2534.LoadLibrary(device):
print("Failed to load library")
return False
deviceId = c_uint(0)
j2534.PassThruOpen(c_void_p(0), deviceId)
channelId = c_uint(0)
j2534.PassThruConnect(
deviceId.value,
ProtocolID.ISO15765,
ConnectFlag.NONE,
BaudRate.ISO15765_500000,
channelId
)
try:
# Set up filter
maskMsg = PassThruMsg(ProtocolID.ISO15765, TxFlag.NONE,
bytes([0x00, 0x00, 0x00, 0x00]))
patternMsg = PassThruMsg(ProtocolID.ISO15765, TxFlag.NONE,
bytes([0x00, 0x00, 0x07, 0xE8]))
flowMsg = PassThruMsg(ProtocolID.ISO15765, TxFlag.NONE,
bytes([0x00, 0x00, 0x07, 0xE0]))
filterId = c_int(0)
j2534.PassThruStartMsgFilter(
channelId.value,
FilterType.FLOW_CONTROL_FILTER,
Utils.ToIntPtr(maskMsg),
Utils.ToIntPtr(patternMsg),
Utils.ToIntPtr(flowMsg),
filterId
)
# Send Clear DTC request (Service 0x14)
clearRequest = PassThruMsg(
ProtocolID.ISO15765,
TxFlag.NONE,
bytes([0x00, 0x00, 0x07, 0xE0, 0x14, 0xFF, 0xFF, 0xFF])
)
txMsgPtr = Utils.ToIntPtr(clearRequest)
numMsgs = c_int(1)
result = j2534.PassThruWriteMsgs(channelId.value, txMsgPtr, numMsgs, 1000)
if result != J2534Err.STATUS_NOERROR:
print(f"Failed to send clear command: {result}")
return False
# Read response
numMsgs = c_int(5)
rxMsgs = cast(create_string_buffer(sizeof(PassThruMsg) * numMsgs.value), c_void_p)
result = j2534.PassThruReadMsgs(channelId.value, rxMsgs, numMsgs, 2000)
if result == J2534Err.STATUS_NOERROR:
messages = Utils.AsMsgList(rxMsgs, numMsgs.value)
for msg in messages:
data = msg.GetBytes()
# 0x54 is positive response to 0x14
if len(data) > 4 and data[4] == 0x54:
print("DTCs cleared successfully!")
return True
elif len(data) > 4 and data[4] == 0x7F:
# Negative response
nrc = data[6] if len(data) > 6 else 0
print(f"Failed to clear DTCs. Negative response code: 0x{nrc:02X}")
return False
print("No response received")
return False
finally:
j2534.PassThruStopMsgFilter(channelId.value, filterId.value)
j2534.PassThruDisconnect(channelId.value)
j2534.PassThruClose(deviceId.value)
j2534.FreeLibrary()
if __name__ == "__main__":
clear_dtcs()from J2534 import *
import time
def monitor_battery_voltage(duration_seconds=60):
"""Monitor battery voltage over time"""
device = J2534Device()
device.FunctionLibrary = "C:\\Path\\To\\J2534.dll"
j2534_ext = J2534FunctionsExtended()
if not j2534_ext.LoadLibrary(device):
print("Failed to load library")
return
deviceId = c_uint(0)
result = j2534_ext.PassThruOpen(c_void_p(0), deviceId)
if result != J2534Err.STATUS_NOERROR:
print(f"Failed to open: {result}")
return
try:
print(f"Monitoring battery voltage for {duration_seconds} seconds...")
print("Time (s) Voltage (V)")
print("-" * 30)
start_time = time.time()
while time.time() - start_time < duration_seconds:
voltage = c_int(0)
result = j2534_ext.ReadBatteryVoltage(deviceId.value, voltage)
if result == J2534Err.STATUS_NOERROR:
voltage_v = voltage.value / 1000.0
elapsed = time.time() - start_time
print(f"{elapsed:6.1f} {voltage_v:5.2f}")
else:
print(f"Error reading voltage: {result}")
time.sleep(1.0) # Read every second
finally:
j2534_ext.PassThruClose(deviceId.value)
j2534_ext.FreeLibrary()
if __name__ == "__main__":
monitor_battery_voltage(duration_seconds=30)from J2534 import *
def safe_operation():
j2534 = J2534Functions()
device = J2534Device()
device.FunctionLibrary = "C:\\Path\\To\\J2534.dll"
try:
if not j2534.LoadLibrary(device):
raise J2534Exception(J2534Err.ERR_DLL_NOT_LOADED)
deviceId = c_uint(0)
result = j2534.PassThruOpen(c_void_p(0), deviceId)
if result != J2534Err.STATUS_NOERROR:
raise J2534Exception(result)
# ... perform operations ...
except J2534Exception as e:
print(f"J2534 Error: {e.Error.name} (0x{e.Error.value:02X})")
print(f"Description: {e}")
finally:
if j2534.IsDLLLoaded:
j2534.FreeLibrary()def handle_j2534_error(error_code: J2534Err):
"""Handle different J2534 errors"""
error_messages = {
J2534Err.ERR_TIMEOUT: "Operation timed out",
J2534Err.ERR_BUFFER_EMPTY: "No messages available",
J2534Err.ERR_BUFFER_FULL: "Buffer is full, cannot send more messages",
J2534Err.ERR_INVALID_CHANNEL_ID: "Invalid channel ID specified",
J2534Err.ERR_ConnectedDevice_NOT_CONNECTED: "Device is not connected",
J2534Err.ERR_FAILED: "General failure occurred"
}
message = error_messages.get(error_code, f"Unknown error: {error_code}")
print(f"Error: {message}")
return error_code != J2534Err.STATUS_NOERROR
# Usage
result = j2534.PassThruReadMsgs(channelId.value, rxMsgs, numMsgs, timeout)
if handle_j2534_error(result):
# Handle error
passProblem: LoadLibrary() returns False
Solutions:
- Verify the DLL path is correct and the file exists
- Check that the DLL is the correct architecture (32-bit vs 64-bit)
- Ensure Python architecture matches DLL architecture
- Verify all DLL dependencies are installed
- Try running as Administrator
import os
dll_path = "C:\\Path\\To\\J2534.dll"
if not os.path.exists(dll_path):
print(f"DLL not found: {dll_path}")
else:
print(f"DLL exists: {dll_path}")Problem: Device not physically connected or drivers not installed
Solutions:
- Check physical connection
- Verify device drivers are installed
- Try a different USB port
- Check Device Manager for any issues
- Restart the device
Problem: No response from vehicle
Solutions:
- Increase timeout value
- Verify vehicle is powered on (ignition on)
- Check physical connections (OBD-II cable)
- Verify correct protocol and baud rate
- Ensure message filters are set up correctly
# Increase timeout
result = j2534.PassThruReadMsgs(
channelId.value,
rxMsgs,
numMsgs,
5000 # 5 second timeout instead of 1 second
)Problem: Message format is incorrect
Solutions:
- Verify message data length
- Check protocol-specific requirements
- Ensure CAN ID format is correct (11-bit vs 29-bit)
- Verify addressing bytes for ISO15765
# For ISO15765, first 4 bytes are CAN ID
# Byte 0-3: CAN ID (big-endian)
# Byte 4+: UDS/OBD data
msg = PassThruMsg(
ProtocolID.ISO15765,
TxFlag.NONE,
bytes([0x00, 0x00, 0x07, 0xE0, # CAN ID 0x7E0
0x01, 0x00]) # Service 0x01, PID 0x00
)Problem: Application crashes or ERR_ACCESS_VIOLATION
Solutions:
- Ensure proper use of ctypes
- Don't free memory managed by the library
- Use
byref()for references - Verify structure sizes match
# Correct usage with byref
deviceId = c_uint(0)
result = j2534.PassThruOpen(c_void_p(0), deviceId) # WRONG - missing byref
result = j2534.PassThruOpen(c_void_p(0), byref(deviceId)) # WRONG - actually this is handled internally
# The library handles byref internally in most cases
# Just pass the c_uint directly
deviceId = c_uint(0)
result = j2534.PassThruOpen(c_void_p(0), deviceId)Enable verbose logging for debugging:
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
def debug_j2534_call(function_name, result):
if result == J2534Err.STATUS_NOERROR:
logger.debug(f"{function_name}: SUCCESS")
else:
logger.error(f"{function_name}: ERROR - {result.name} (0x{result.value:02X})")
# Usage
result = j2534.PassThruConnect(deviceId.value, protocol, flags, baud, channelId)
debug_j2534_call("PassThruConnect", result)# Open multiple channels for different protocols
can_channel = c_uint(0)
iso9141_channel = c_uint(0)
# Connect to CAN
j2534.PassThruConnect(
deviceId.value,
ProtocolID.CAN,
ConnectFlag.NONE,
BaudRate.CAN_500000,
can_channel
)
# Connect to ISO9141
j2534.PassThruConnect(
deviceId.value,
ProtocolID.ISO9141,
ConnectFlag.NONE,
BaudRate.ISO9141_10400,
iso9141_channel
)
# Use both channels...
# Disconnect both
j2534.PassThruDisconnect(can_channel.value)
j2534.PassThruDisconnect(iso9141_channel.value)# Send periodic keep-alive message
tester_present = PassThruMsg(
ProtocolID.ISO15765,
TxFlag.NONE,
bytes([0x00, 0x00, 0x07, 0xE0, 0x3E, 0x00]) # Tester Present
)
msgId = c_int(0)
timeInterval = 2000 # Every 2 seconds
result = j2534.PassThruStartPeriodicMsg(
channelId.value,
Utils.ToIntPtr(tester_present),
msgId,
timeInterval
)
# Later, stop the periodic message
j2534.PassThruStopPeriodicMsg(channelId.value, msgId.value)# Block specific CAN IDs
blockMask = PassThruMsg(ProtocolID.CAN, TxFlag.NONE,
bytes([0xFF, 0xFF, 0xFF, 0xFF])) # Check all bits
blockPattern = PassThruMsg(ProtocolID.CAN, TxFlag.NONE,
bytes([0x00, 0x00, 0x05, 0x00])) # Block ID 0x500
filterId = c_int(0)
j2534.PassThruStartMsgFilter(
channelId.value,
FilterType.BLOCK_FILTER,
Utils.ToIntPtr(blockMask),
Utils.ToIntPtr(blockPattern),
c_void_p(0),
filterId
)
# Pass specific range of IDs
passMask = PassThruMsg(ProtocolID.CAN, TxFlag.NONE,
bytes([0xFF, 0xFF, 0xF0, 0x00])) # Check upper bits
passPattern = PassThruMsg(ProtocolID.CAN, TxFlag.NONE,
bytes([0x00, 0x00, 0x70, 0x00])) # Pass 0x700-0x7FF
filterId2 = c_int(0)
j2534.PassThruStartMsgFilter(
channelId.value,
FilterType.PASS_FILTER,
Utils.ToIntPtr(passMask),
Utils.ToIntPtr(passPattern),
c_void_p(0),
filterId2
)from J2534 import *
def configure_channel(j2534_ext, channelId):
"""Configure channel parameters"""
# Get current configuration
config_get = [
SConfig(ConfigParameter.DATA_RATE, 0),
SConfig(ConfigParameter.LOOPBACK, 0)
]
result = j2534_ext.GetConfig(channelId, config_get)
if result == J2534Err.STATUS_NOERROR:
print(f"Current baud rate: {config_get[0].Value}")
print(f"Loopback: {config_get[1].Value}")
# Set new configuration
config_set = [
SConfig(ConfigParameter.LOOPBACK, 0), # Disable loopback
SConfig(ConfigParameter.ISO15765_BS, 8), # Block size
SConfig(ConfigParameter.ISO15765_STMIN, 10) # 10ms separation time
]
result = j2534_ext.SetConfig(channelId, config_set)
if result == J2534Err.STATUS_NOERROR:
print("Configuration updated successfully")
else:
print(f"Failed to set configuration: {result}")# Set programming voltage (for ECU flashing)
result = j2534.PassThruSetProgrammingVoltage(
deviceId.value,
PinNumber.PIN_6,
PinVoltage.FEPS_VOLTAGE # 18V
)
if result == J2534Err.STATUS_NOERROR:
print("Programming voltage set to 18V on Pin 6")
# Perform programming operations...
# Turn off programming voltage
j2534.PassThruSetProgrammingVoltage(
deviceId.value,
PinNumber.PIN_6,
PinVoltage.VOLTAGE_OFF
)def get_device_version(j2534, deviceId):
"""Read device firmware and API versions"""
firmware = create_string_buffer(80)
dll_version = create_string_buffer(80)
api_version = create_string_buffer(80)
result = j2534.PassThruReadVersion(
deviceId.value,
cast(firmware, c_void_p),
cast(dll_version, c_void_p),
cast(api_version, c_void_p)
)
if result == J2534Err.STATUS_NOERROR:
print(f"Firmware: {firmware.value.decode('ascii', errors='ignore')}")
print(f"DLL Version: {dll_version.value.decode('ascii', errors='ignore')}")
print(f"API Version: {api_version.value.decode('ascii', errors='ignore')}")-
Always Check Return Values: Every J2534 function returns a status code. Always check it.
-
Proper Resource Management: Use try-finally blocks to ensure resources are cleaned up.
-
Timeouts: Set appropriate timeouts based on expected response times.
-
Message Filtering: Set up proper filters before reading messages to avoid buffer overflow.
-
Error Handling: Implement robust error handling for production code.
-
Testing: Test thoroughly with your specific hardware before deployment.
-
Documentation: Keep SAE J2534 specification handy for reference.
Contributions are welcome! Please ensure:
- Exact naming conventions are maintained (for C# compatibility)
- All changes are tested with real J2534 hardware
- Documentation is updated for new features
- Error handling is robust
For issues specific to this Python implementation, please check:
- This README for solutions
- The SAE J2534 specification
- Your J2534 device manufacturer's documentation
- Complete Python port from C#
- All J2534 v04.04 functions implemented
- Extended functions for battery voltage, buffer management
- Full support for all protocols
- Comprehensive error handling
- Utility classes for common operations
Note: This library requires a physical J2534-compliant PassThru device and its associated drivers to function. It is intended for automotive diagnostics and ECU programming professionals.