├── .gitattributes ├── AutoComm.py ├── AutoJ2534 ├── EcuParameters.py ├── Interface.py ├── __init__.py └── __pycache__ │ ├── EcuParameters.cpython-310.pyc │ ├── EcuParameters.cpython-311.pyc │ ├── Interface.cpython-310.pyc │ ├── Interface.cpython-311.pyc │ ├── __init__.cpython-310.pyc │ └── __init__.cpython-311.pyc ├── J2534 ├── Define.py ├── LICENSE ├── Registry.py ├── __init__.py ├── dll.py └── wrapper.py └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /AutoComm.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from AutoJ2534.Interface import j2534_communication 3 | 4 | # auto connect example... 5 | j2534_communication.auto_connect() 6 | 7 | # can bus example... 8 | print(j2534_communication.transmit_and_receive_message([0x1a, 0x90])) 9 | 10 | # sci bus example... 11 | # print(j2534_communication.transmit_and_receive_message([0x2a, 0x0f])) 12 | -------------------------------------------------------------------------------- /AutoJ2534/EcuParameters.py: -------------------------------------------------------------------------------- 1 | class ConnectionConfig: 2 | def __init__(self, name, tx_delay, rx_delay, tx_id, rx_id, mask, connect_flag, tx_flag, baud_rate, protocol_id, 3 | protocol_name, comm_check, t1_max, t2_max, t4_max, t5_max): 4 | self.name = name 5 | self.tx_delay = tx_delay 6 | self.rx_delay = rx_delay 7 | self.tx_id = tx_id 8 | self.rx_id = rx_id 9 | self.mask = mask 10 | self.connect_flag = connect_flag 11 | self.tx_flag = tx_flag 12 | self.baud_rate = baud_rate 13 | self.protocol_id = protocol_id 14 | self.protocol_name = protocol_name 15 | self.comm_check = comm_check 16 | self.t1_max = t1_max 17 | self.t2_max = t2_max 18 | self.t4_max = t4_max 19 | self.t5_max = t5_max 20 | 21 | 22 | class Connections: 23 | CHRYSLER_ECU = { 24 | 'chrys1': ConnectionConfig( 25 | 'CHRYSLER ECU CAN 11-BIT', 26 | 0, 27 | 500, 28 | 0x7E0, 29 | 0x7E8, 30 | 0xFFFFFFFF, 31 | 0, 32 | 0X40, 33 | 500000, 34 | 6, 35 | 'ISO15765', 36 | [0x1a, 0x87], 37 | None, 38 | None, 39 | None, 40 | None 41 | ), 42 | 'chrys2': ConnectionConfig( 43 | 'CHRYSLER ECU CAN 29-BIT', 44 | 0, 45 | 500, 46 | 0x18DA10F1, 47 | 0x18DAF110, 48 | 0xFFFFFFFF, 49 | 0x800, 50 | 0X140, 51 | 500000, 52 | 6, 53 | 'ISO15765', 54 | [0x1a, 0x87], 55 | None, 56 | None, 57 | None, 58 | None 59 | ), 60 | 'chrys3': ConnectionConfig( 61 | 'CHRYSLER ECU SCI A ENGINE', 62 | 500, 63 | 1000, 64 | 0, 65 | 0, 66 | 0, 67 | 0, 68 | 0, 69 | 7813, 70 | 7, 71 | 'SCI_A_ENGINE', 72 | [0x2a, 0x0f], 73 | None, 74 | None, 75 | 200, 76 | None 77 | ), 78 | 'chrys4': ConnectionConfig( 79 | 'CHRYSLER ECU SCI B ENGINE', 80 | 500, 81 | 1000, 82 | 0, 83 | 0, 84 | 0, 85 | 0, 86 | 0, 87 | 7813, 88 | 9, 89 | 'SCI_B_ENGINE', 90 | [0x22, 0x20, 0x07, 0x49], 91 | 75, 92 | 5, 93 | 50, 94 | 1 95 | ), 96 | 'chrys5': ConnectionConfig( 97 | 'CHRYSLER ECU SCI B CUMMINS', 98 | 500, 99 | 1000, 100 | 0, 101 | 0, 102 | 0, 103 | 0, 104 | 0, 105 | 7813, 106 | 9, 107 | 'SCI_B_ENGINE', 108 | [0x2a, 0x0f], 109 | 75, 110 | 50, 111 | 50, 112 | 10 113 | ), 114 | 'chrys6': ConnectionConfig( 115 | 'CHRYSLER TIPM', 116 | 0, 117 | 500, 118 | 0X620, 119 | 0X504, 120 | 0xFFFFFFFF, 121 | 0, 122 | 0X40, 123 | 500000, 124 | 6, 125 | 'ISO15765', 126 | [0x1a, 0x87], 127 | None, 128 | None, 129 | None, 130 | None 131 | ), 132 | 'chrys7': ConnectionConfig( 133 | 'CHRYSLER BCM', 134 | 0, 135 | 500, 136 | 0X620, 137 | 0X504, 138 | 0xFFFFFFFF, 139 | 0, 140 | 0X40, 141 | 500000, 142 | 6, 143 | 'ISO15765', 144 | [0x22, 0xf1, 0x90], 145 | None, 146 | None, 147 | None, 148 | None 149 | ), 150 | 'chrys8': ConnectionConfig( 151 | 'CHRYSLER ECU SCI B TRANS', 152 | 500, 153 | 1000, 154 | 0, 155 | 0, 156 | 0, 157 | 0, 158 | 0, 159 | 7813, 160 | 10, 161 | 'SCI_B_TRANS', 162 | [0x01, 0x00], 163 | 75, 164 | 5, 165 | 50, 166 | 1 167 | ), 168 | 'chrys9': ConnectionConfig( 169 | 'CHRYSLER ECU SCI A TRANS', 170 | 500, 171 | 1000, 172 | 0, 173 | 0, 174 | 0, 175 | 0, 176 | 0, 177 | 7813, 178 | 8, 179 | 'SCI_A_TRANS', 180 | [0x2a, 0x0f], 181 | None, 182 | None, 183 | None, 184 | None 185 | ), 186 | 'chrys10': ConnectionConfig( 187 | 'CHRYSLER TRANS CAN 11-BIT', 188 | 0, 189 | 500, 190 | 0X7E1, 191 | 0X7E9, 192 | 0XFFFFFFFF, 193 | 0, 194 | 0X40, 195 | 500000, 196 | 6, 197 | 'ISO15765', 198 | [0x1a, 0x87], 199 | None, 200 | None, 201 | None, 202 | None 203 | ) 204 | } 205 | -------------------------------------------------------------------------------- /AutoJ2534/Interface.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import winreg 3 | import platform 4 | import J2534 5 | from AutoJ2534.EcuParameters import Connections 6 | 7 | 8 | class J2534Communications: 9 | def __init__(self): 10 | 11 | self.tool_index_found = None 12 | self.loops = None 13 | self.transmit_delay = 1000 14 | self.receive_delay = 1000 15 | 16 | self.tool_open_flag = False 17 | self.channel_open_flag = False 18 | 19 | self.com_found = None 20 | self.connection_keys = None 21 | self.volts = None 22 | self._protcol_string = None 23 | self._comm_check = None 24 | self._mask_id = None 25 | self._tx_flag = None 26 | self._tx_id = None 27 | self._rx_id = None 28 | self._baud_rate = None 29 | self._connect_flag = None 30 | self._protocol = None 31 | self._ecu_filter = None 32 | self._channel_id = None 33 | self._device_id = None 34 | self.check_characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" 35 | self.can_7f_codes = { 36 | # Negative Response Codes 37 | "10": "General Reject", 38 | "11": "Service Not Supported", 39 | "12": "Function Not Supported/Invalid Format", 40 | "21": "Busy/Repeat Request", 41 | "22": "Conditions Not Correct", 42 | "24": "Request Sequence Error", 43 | "26": "Failure Prevents Execution Of Request Action", 44 | "31": "Request Out Of Range", 45 | "33": "Security Access Denied/Security Access Requested", 46 | "35": "Invalid Key", 47 | "36": "Exceed Number Of Attempts", 48 | "37": "Required Time Delay Not Expired", 49 | "40": "Download Not Accepted", 50 | "50": "Upload Not Accepted", 51 | "70": "Upload Download Not Accepted", 52 | "71": "Transfer Suspended", 53 | "72": "General Programming Failure", 54 | "73": "Wrong Block Sequence Counter", 55 | "78": "Request Correctly Received/Response Pending", 56 | "7E": "Sub Function Not Supported In Active Session", 57 | "7F": "Service Not Supported In Active Session", 58 | "80": "Service Not Supported In Active Diagnostic Session", 59 | "92": "Voltage Too High", 60 | "93": "Voltage Too Low", 61 | "9A": "Data Decompression Failed", 62 | "9B": "Data Decryption Failed", 63 | "A0": "ECU Not Responding", 64 | "A1": "ECU-Address Unknown", 65 | "FA": "Revoked Key", 66 | "FB": "Expired Key", 67 | } 68 | 69 | @staticmethod 70 | def get_interfaces() -> dict: 71 | """Enumerate all registered J2534 04.04 Pass-Thru interface DLLs 72 | Returns: 73 | dict: A dict mapping display names of any registered J2534 74 | Pass-Thru DLLs to their absolute filepath. 75 | The name can be used in user-facing GUI elements to allow 76 | selection of a particular Pass-Thru device, and filepath can 77 | be passed to :func:`load_interface` to instantiate a 78 | :class:`J2534Dll` wrapping the desired DLL. 79 | """ 80 | j2534_dictionary = {} 81 | 82 | registry_path = r"Software\\Wow6432Node\\PassThruSupport.04.04\\" 83 | 84 | if platform.architecture()[0] == "32bit": 85 | registry_path = r"Software\\PassThruSupport.04.04\\" 86 | 87 | base_key = winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, registry_path) 88 | count = winreg.QueryInfoKey(base_key)[0] 89 | 90 | for i in range(count): 91 | device_key = winreg.OpenKeyEx(base_key, winreg.EnumKey(base_key, i)) 92 | name = winreg.QueryValueEx(device_key, "Name")[0] 93 | function_library = winreg.QueryValueEx(device_key, "FunctionLibrary")[0] 94 | j2534_dictionary[name] = function_library 95 | 96 | return j2534_dictionary 97 | 98 | def clear_rx(self): 99 | return J2534.clear_receive_buffer(self._channel_id) 100 | 101 | def clear_tx(self): 102 | return J2534.clear_transmit_buffer(self._channel_id) 103 | 104 | @staticmethod 105 | def numbered_tool_list(): 106 | # this will print list of j2534 devices with there index number next to them... 107 | try: 108 | devices = J2534.get_list_j2534_devices() 109 | for index, device in enumerate(devices): 110 | if 'Error:' in device[0]: 111 | print(device[0]) 112 | return True 113 | print(f'{index} = {device[0]}') 114 | return True 115 | except Exception as e: 116 | print(f'Error: {e}') 117 | raise e # raise the exception to let the caller handle it 118 | 119 | @staticmethod 120 | def tool_list(): 121 | try: 122 | return J2534.get_list_j2534_devices() 123 | except Exception as e: 124 | print(f'Error: {e}') 125 | return print(f'Error: tool list') 126 | 127 | def tool_info(self): 128 | try: 129 | return J2534.pt_read_version(self._device_id) 130 | except Exception as e: 131 | print(f'Error: {e}') 132 | return print(f'Error: tool info') 133 | 134 | def check_volts(self): 135 | self.volts = J2534.read_battery_volts(self._device_id) 136 | return self.volts if self.volts and 11.0 < self.volts < 14.7 else False 137 | 138 | def close(self) -> bool: 139 | self.tool_open_flag = False 140 | J2534.pt_close(self._device_id) 141 | return True 142 | 143 | def disconnect(self) -> bool: 144 | # pt_close close open channel to j2534 tool. 145 | if not J2534.pt_disconnect(self._channel_id): 146 | return False 147 | # set flag to false after channel is closed 148 | self.channel_open_flag = False 149 | return True 150 | 151 | def _build_connection_library(self, library_name: str): 152 | # make var to shorten up the calls to dict. 153 | param = Connections.CHRYSLER_ECU.get(library_name, False) # get connection parameters from dict. 154 | self._key_name = param.name 155 | self.transmit_delay = param.tx_delay # transmit delay for tx only functions. 156 | self.receive_delay = param.rx_delay # receive delay for rx only functions. 157 | self._protcol_string = param.protocol_name # communication protocol name selected. 158 | self._protocol = param.protocol_id # communication protocol selected. 159 | self._connect_flag = param.connect_flag # connection flag if needed most likely not needed. 160 | self._baud_rate = param.baud_rate # communication protocol baud-rate selected. 161 | self._mask_id = param.mask # data filter mask selected, this pertains to data and not the address id. 162 | self._rx_id = param.rx_id # rx address that is allowed to be read all other will be ignored 163 | self._tx_id = param.tx_id # tx address that is allowed to be read all other will be ignored 164 | self._tx_flag = param.tx_flag # will set some spec. functions like can frame pad and prog voltage. 165 | self._comm_check = param.comm_check # communication check to verify communication is working 166 | if self._protocol in [7, 8, 9, 10]: # if protocol is J1850 or sci set protocol related attributes. 167 | self._t1_max = param.t1_max # inter frame rate delays if it pertains to this protocol. 168 | self._t2_max = param.t2_max # inter frame rate delays if it pertains to this protocol. 169 | self._t4_max = param.t4_max # inter frame rate delays if it pertains to this protocol. 170 | self._t5_max = param.t5_max # inter frame rate delays if it pertains to this protocol. 171 | return True 172 | 173 | def open_j2534_interface(self, index_of_tool: int) -> bool: 174 | try: 175 | if self.tool_open_flag: # if tool is already open no need to try to open again. 176 | return True 177 | J2534.set_j2534_device_to_connect(index_of_tool) # set device index number to open connection to. 178 | 179 | self._device_id = J2534.pt_open() # open connection to j2534 tool this will return device id number... 180 | if self._device_id is False: 181 | return False 182 | 183 | if not self.check_volts(): # if voltage is not within range return False. 184 | return False 185 | 186 | self.tool_open_flag = True # if tool opens successfully set flag. 187 | return True 188 | except Exception as e: 189 | return False 190 | 191 | def establish_connection(self) -> bool: 192 | try: 193 | # pt_connect opens channel to selected communication protocol if successful will return channel id. 194 | self._channel_id = J2534.pt_connect(self._device_id, self._protocol, self._connect_flag, self._baud_rate) 195 | 196 | if not self._channel_id: # if pt_connect is not successful return False. 197 | self.channel_open_flag = False # if channel id is not returned set flag to False. 198 | return False 199 | 200 | self.channel_open_flag = True # if channel id is returned set flag to True. 201 | return True 202 | except Exception as e: 203 | return False 204 | 205 | def _tmax_delays(self) -> bool: 206 | # set inter frame rate delays if it pertains to this protocol. 207 | if self._protocol in [7, 8, 9, 10]: 208 | tmax = [self._t1_max, self._t2_max, self._t4_max, self._t5_max] 209 | for cnt, x in enumerate(tmax, start=26): 210 | if x: 211 | J2534.pt_set_config(self._channel_id, [[cnt, x]]) 212 | return True 213 | 214 | def _set_ecu_filter(self): 215 | # pt_start_ecu_filter set filters of tx and rx id only allow the id's of choice to be read. 216 | self._ecu_filter = J2534.pt_start_ecu_filter( 217 | self._channel_id, 218 | self._protocol, # communication protocol selected. 219 | self._mask_id, # data filter mask selected, this pertains to data and not the address id. 220 | self._rx_id, # rx address that is allowed to be read all other will be ignored 221 | self._tx_id, # tx address that is allowed to be read all other will be ignored 222 | self._tx_flag, # will set some spec. functions like can frame pad and prog voltage. 223 | ) 224 | return self._ecu_filter # returns filter id if successful. 225 | 226 | def open_communication(self, index_of_tool: int, library_name: str) -> bool: 227 | if not self._build_connection_library(library_name): 228 | return False 229 | 230 | if not self.open_j2534_interface(index_of_tool): 231 | return False 232 | 233 | if not self.establish_connection(): 234 | return False 235 | 236 | return self._set_ecu_filter() if self._tmax_delays() else False 237 | 238 | def _transmit_only_can_message(self, data_to_transmit): 239 | # set message structure for transmit and receive. 240 | tx = J2534.PassThruMsgBuilder(self._protocol, self._tx_flag) 241 | # set data in buffer ready to tx. 242 | tx.set_identifier_and_data(self._tx_id, data_to_transmit) 243 | # Transmit one message. 244 | return J2534.pt_write_message(self._channel_id, tx, 1, self.transmit_delay) 245 | 246 | def _receive_only_can_message(self, transmitted_data, loops=0): 247 | rx = J2534.PassThruMsgBuilder(self._protocol, self._tx_flag) # set message structure for receive. 248 | rx_output = rx.dump_output() # dump output to var. 249 | check_byte = rx_output[8:10] 250 | error_byte = rx_output[12:14] 251 | 252 | for _ in range(3 + loops): # loop 3 times plus any additional loops. 253 | 254 | # read message from buffer. 255 | if J2534.pt_read_message(self._channel_id, rx, 1, self.receive_delay) == 16: 256 | return False 257 | 258 | # rx.status response descriptions: 259 | # 0 = msg read successfully 260 | # 2 = start of message or first frame 261 | # 4 = rx break 262 | # 8 = rx break 263 | # 9 = tx indication 264 | # 100 = can 29bit id 265 | # 102 = start of message 266 | # 109 = tx message type + tx indication + can 29bit id 267 | # 256 = can 29 bit + msg read successfully 268 | # 258 = can 29 bit + tx indication 269 | # 265 = tx indication 270 | 271 | if rx.RxStatus in [2, 9, 102, 258, 265]: 272 | continue 273 | 274 | # if rx.status is 0 or 256 we are done reading from buffer! time to process data. 275 | if rx.RxStatus in [0, 256]: 276 | 277 | if check_byte == '7F': # 7F is negative response. 278 | if error_byte == '78': 279 | continue 280 | 281 | # check if error/7F is returned, lookup failure code and return failure string. 282 | return self.can_7f_codes.get(error_byte, 'Function failed/no definition') 283 | 284 | # if we receive positive response return data received. 285 | if check_byte == (hex(transmitted_data[0] + 0x40)[2:].upper()): # first byte + 64 is pos response 286 | # return data[8:] less first 8 bytes of response which is recv address. 287 | return rx_output[8:] 288 | return False 289 | 290 | def _transmit_and_receive_can_message(self, data_to_transmit, loops=0): 291 | # set message structure for transmit and receive. 292 | tx = J2534.PassThruMsgBuilder(self._protocol, self._tx_flag) 293 | rx = J2534.PassThruMsgBuilder(self._protocol, self._tx_flag) 294 | 295 | # set data in buffer ready to tx. 296 | tx.set_identifier_and_data(self._tx_id, data_to_transmit) 297 | 298 | try: 299 | # Transmit one message. 300 | if J2534.pt_write_message(self._channel_id, tx, 1, self.transmit_delay) is False: 301 | return False 302 | 303 | for _ in range(3 + int(loops)): 304 | if J2534.pt_read_message(self._channel_id, rx, 1, self.receive_delay) in [16]: 305 | return False 306 | # rx.status response descriptions: 307 | # 0 = msg read successfully 308 | # 2 = start of message or first frame 309 | # 4 = rx break 310 | # 8 = rx break 311 | # 9 = tx indication 312 | # 100 = can 29bit id 313 | # 102 = start of message 314 | # 109 = tx message type + tx indication + can 29bit id 315 | # 256 = can 29 bit + msg read successfully 316 | # 258 = can 29 bit + tx indication 317 | # 265 = tx indication 318 | 319 | # if rx.status is 2,9,109,102 == 2/102 =start of message, 9/109 =tx indication, continue loop. 320 | if rx.RxStatus in [2, 9, 102, 258, 265]: 321 | continue 322 | 323 | # if rx.status is 0 we are done reading from buffer! time to process data. 324 | if rx.RxStatus in [0, 256]: 325 | 326 | if rx.dump_output()[8:10] in ['7F'] and rx.dump_output()[12:14] == '78': 327 | continue 328 | 329 | # check if error/7F is returned, lookup failure code and return failure string. 330 | if rx.dump_output()[8:10] in ['7F']: 331 | error_id = rx.dump_output()[12:14] 332 | return self.can_7f_codes.get(error_id, 'Function failed/no definition') 333 | 334 | # first byte of response + 64 is pos response 335 | positive_response = (hex(data_to_transmit[0] + 0x40)[2:].upper()) 336 | 337 | # if we receive positive response return data received. 338 | if rx.dump_output()[8:10] == positive_response: 339 | # return data[8:] less first 8 bytes of response which is recv address. 340 | return rx.dump_output()[8:] 341 | except Exception as e: 342 | return False 343 | return False 344 | 345 | def _transmit_and_receive_sci_message(self, data_to_transmit): 346 | # set message structure for transmit and receive. 347 | tx = J2534.PassThruMsgBuilder(self._protocol, self._tx_flag) 348 | rx = J2534.PassThruMsgBuilder(self._protocol, self._tx_flag) 349 | 350 | # set data in buffer ready to tx. 351 | tx.build_transmit_data_block(data_to_transmit) 352 | 353 | # Transmit one message. 354 | if J2534.pt_write_message(self._channel_id, tx, 1, self.transmit_delay) is False: 355 | return False 356 | 357 | var0 = J2534.pt_read_message(self._channel_id, rx, 1, self.receive_delay) 358 | if var0 in [16]: 359 | return False 360 | 361 | return rx.dump_output() if rx.DataSize > 1 else False 362 | 363 | def transmit_and_receive_message(self, data_to_transmit: list, loops=0): 364 | self.loops = loops 365 | match self._protocol: 366 | case 6: 367 | return self._transmit_and_receive_can_message(data_to_transmit, self.loops) 368 | case 7: 369 | return self._transmit_and_receive_sci_message(data_to_transmit) 370 | case 8: 371 | return self._transmit_and_receive_sci_message(data_to_transmit) 372 | case 9: 373 | return self._transmit_and_receive_sci_message(data_to_transmit) 374 | case 10: 375 | return self._transmit_and_receive_sci_message(data_to_transmit) 376 | 377 | def transmit_only(self, data_to_transmit: list): 378 | match self._protocol: 379 | case 6: 380 | return self._transmit_only_can_message(data_to_transmit) 381 | case 7: 382 | return self._transmit_and_receive_sci_message(data_to_transmit) 383 | case 8: 384 | return self._transmit_and_receive_sci_message(data_to_transmit) 385 | case 9: 386 | return self._transmit_and_receive_sci_message(data_to_transmit) 387 | case 10: 388 | return self._transmit_and_receive_sci_message(data_to_transmit) 389 | 390 | def receive_only(self, transmitted_data, loops=0): 391 | self.loops = loops 392 | match self._protocol: 393 | case 6: 394 | return self._receive_only_can_message(transmitted_data, loops=0) 395 | case 7: 396 | return self._transmit_and_receive_sci_message(transmitted_data) 397 | case 8: 398 | return self._transmit_and_receive_sci_message(transmitted_data) 399 | case 9: 400 | return self._transmit_and_receive_sci_message(transmitted_data) 401 | case 10: 402 | return self._transmit_and_receive_sci_message(transmitted_data) 403 | 404 | def tool_search(self): 405 | # search for index of connected j2534 device... 406 | for x in range(20): # loop through all j2534 devices found in registry... 407 | 408 | if self.open_j2534_interface(x): # test all indexes of tools to see which one responds... 409 | 410 | self.tool_index_found = x # save index of tool if it responds... 411 | 412 | self.close() # close connection of tool... 413 | 414 | return self.tool_index_found # return saved index of tool that responded... 415 | 416 | return False 417 | 418 | def auto_connect(self): 419 | # search for index of connected j2534 device... 420 | tool_index = self.tool_search() 421 | 422 | # dictionary of connection parameters only use the first 7 keys... 423 | connection_keys = list(Connections.CHRYSLER_ECU.keys())[:7] # get list of connection parameters from dict... 424 | try: 425 | for connection_key in connection_keys: # loop through connection keys... 426 | 427 | if self.open_communication(tool_index, 428 | connection_key): # test connection params to see if ecu responds... 429 | tool_info = self.tool_info() # get info of connected j2534 tool... 430 | 431 | if self.transmit_and_receive_message(self._comm_check): # if communication is successful... 432 | print(f'Found and connected to {tool_info[0]}') 433 | print(f'Communication successful with {self._key_name}') 434 | self.com_found = connection_key 435 | 436 | # if we get a successful connection return all connection information... 437 | return [tool_index, connection_key, self._key_name, tool_info[0], tool_info[1], tool_info[2]] 438 | 439 | self.close() # after testing all keys if no response close connection to j2534 device... 440 | return False 441 | 442 | except Exception as e: 443 | return False 444 | 445 | 446 | j2534_communication = J2534Communications() 447 | -------------------------------------------------------------------------------- /AutoJ2534/__init__.py: -------------------------------------------------------------------------------- 1 | from .EcuParameters import Connections 2 | -------------------------------------------------------------------------------- /AutoJ2534/__pycache__/EcuParameters.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keenanlaws/Python-J2534-Interface/4e784b91b688d59df4f45d2e5f6ffbb6d8a88393/AutoJ2534/__pycache__/EcuParameters.cpython-310.pyc -------------------------------------------------------------------------------- /AutoJ2534/__pycache__/EcuParameters.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keenanlaws/Python-J2534-Interface/4e784b91b688d59df4f45d2e5f6ffbb6d8a88393/AutoJ2534/__pycache__/EcuParameters.cpython-311.pyc -------------------------------------------------------------------------------- /AutoJ2534/__pycache__/Interface.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keenanlaws/Python-J2534-Interface/4e784b91b688d59df4f45d2e5f6ffbb6d8a88393/AutoJ2534/__pycache__/Interface.cpython-310.pyc -------------------------------------------------------------------------------- /AutoJ2534/__pycache__/Interface.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keenanlaws/Python-J2534-Interface/4e784b91b688d59df4f45d2e5f6ffbb6d8a88393/AutoJ2534/__pycache__/Interface.cpython-311.pyc -------------------------------------------------------------------------------- /AutoJ2534/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keenanlaws/Python-J2534-Interface/4e784b91b688d59df4f45d2e5f6ffbb6d8a88393/AutoJ2534/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /AutoJ2534/__pycache__/__init__.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keenanlaws/Python-J2534-Interface/4e784b91b688d59df4f45d2e5f6ffbb6d8a88393/AutoJ2534/__pycache__/__init__.cpython-311.pyc -------------------------------------------------------------------------------- /J2534/Define.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class ProtocolID(Enum): 5 | J1850VPW = 1 6 | J1850PWM = 2 7 | ISO9141 = 3 8 | ISO14230 = 4 9 | CAN = 5 10 | ISO15765 = 6 11 | SCI_A_ENGINE = 7 12 | SCI_A_TRANS = 8 13 | SCI_B_ENGINE = 9 14 | SCI_B_TRANS = 10 15 | 16 | 17 | class Flags(Enum): 18 | # Flags.value(Flags.CAN_29BIT_ID,Flags.CAN_ID_BOTH) 19 | NONE = 0 20 | CAN_29BIT_ID = 0x100 21 | ISO9141_NO_CHECKSUM = 0x200 22 | NO_CHECKSUM = 0x200 23 | CAN_ID_BOTH = 0x800 24 | ISO9141_K_LINE_ONLY = 0x1000 25 | LISTEN_ONLY_DT = 0x10000000 26 | SNIFF_MODE = 0x10000000 27 | ISO9141_FORD_HEADER = 0x20000000 28 | ISO9141_NO_CHECKSUM_DT = 0x40000000 29 | 30 | 31 | class BaudRate(Enum): 32 | SCI = 7813 33 | SCI_HIGHSPEED = 62500 34 | ISO9141_10400 = 10400 35 | ISO9141_10000 = 10000 36 | ISO14230_10400 = 10400 37 | ISO14230_10000 = 10000 38 | J1850PWM_41600 = 41600 39 | J1850PWM_83300 = 83300 40 | J1850VPW_10400 = 10400 41 | J1850VPW_41600 = 41600 42 | CAN_125k = 125000 43 | CAN_250k = 250000 44 | CAN_500k = 500000 45 | 46 | 47 | class FilterType(object): 48 | PASS_FILTER = 0x00000001 49 | BLOCK_FILTER = 0x00000002 50 | FLOW_CONTROL_FILTER = 0x00000003 51 | 52 | 53 | class Voltage(Enum): 54 | SHORT_TO_GROUND = 0xFFFFFFFF 55 | VOLTAGE_OFF = 0xFFFFFFFE 56 | 57 | 58 | class IoctlID(object): 59 | GET_CONFIG = 0x01 60 | SET_CONFIG = 0x02 61 | READ_VBATT = 0x03 62 | FIVE_BAUD_INIT = 0x04 63 | FAST_INIT = 0x05 64 | CLEAR_TX_BUFFER = 0x07 65 | CLEAR_RX_BUFFER = 0x08 66 | CLEAR_PERIODIC_MSGS = 0x09 67 | CLEAR_MSG_FILTERS = 0x0A 68 | CLEAR_FUNCT_MSG_LOOKUP_TABLE = 0x0B 69 | ADD_TO_FUNCT_MSG_LOOKUP_TABLE = 0x0C 70 | DELETE_FROM_FUNCT_MSG_LOOKUP_TABLE = 0x0D 71 | READ_PROG_VOLTAGE = 0x0E 72 | DATA_RATE = 0x01 # 5 500000 # Baud rate value used for vehicle network. No default value specified. 73 | LOOPBACK = 0x03 # 0(OFF)/1(ON) # 0 = Do not echo transmitted messages to the Receive queue. 1 = Echo transmitted messages to the Receive queue. 74 | NODE_ADDRESS = 0x04 # 0x00-0xFF # J1850PWM specific, physical address for node of interest in the vehicle network. Default is no nodes are recognized by scan tool. 75 | NETWORK_LINE = 0x05 # 0(BUS_NORMAL)/1(BUS_PLUS)/2(BUS_MINUS) # J1850PWM specific, network line(s) active during message transfers. Default value is 0(BUS_NORMAL). 76 | P1_MIN = 0x06 # 0x0-0xFFFF # ISO-9141/14230 specific, min. ECU inter-byte time for responses [02.02-API: ms]. Default value is 0 ms. 04.04-API: NOT ADJUSTABLE, 0ms. 77 | P1_MAX = 0x07 # 0x0/0x1-0xFFFF # ISO-9141/14230 specific, max. ECU inter-byte time for responses [02.02-API: ms, 04.04-API: *0.5ms]. Default value is 20 ms. 78 | P2_MIN = 0x08 # 0x0-0xFFFF # ISO-9141/14230 specific, min. ECU response time to a tester request or between ECU responses [02.02-API: ms, 04.04-API: *0.5ms]. 04.04-API: NOT ADJUSTABLE, 0ms. Default value is 25 ms. 79 | P2_MAX = 0x09 # 0x0-0xFFFF # ISO-9141/14230 specific, max. ECU response time to a tester request or between ECU responses [02.02-API: ms, 04.04-API: *0.5ms]. 04.04-API: NOT ADJUSTABLE, all messages up to P3_MIN are receoved. Default value is 50 ms. 80 | P3_MIN = 0x0A # 0x0-0xFFFF # ISO-9141/14230 specific, min. ECU response time between end of ECU response and next tester request [02.02-API: ms, 04.04-API: *0.5ms]. Default value is 55 ms. 81 | P3_MAX = 0x0B # 0x0-0xFFFF # ISO-9141/14230 specific, max. ECU response time between end of ECU response and next tester request [02.02-API: ms, 04.04-API: *0.5ms]. 04.04-API: NOT ADJUSTABLE, messages can be sent at anytime after P3_MIN. Default value is 5000 ms. 82 | P4_MIN = 0x0C # 0x0-0xFFFF # ISO-9141/14230 specific, min. tester inter-byte time for a request [02.02-API: ms, 04.04-API: *0.5ms]. Default value is 5 ms. 83 | P4_MAX = 0x0D # 0x0-0xFFFF # ISO-9141/14230 specific, max. tester inter-byte time for a request [02.02-API: ms, 04.04-API: *0.5ms]. 04.04-API: NOT ADJUSTABLE, P4_MIN is always used. Default value is 20 ms. 84 | W1 = 0x0E # 0x0-0xFFFF # ISO 9141 specific, max. time [ms] from the address byte end to synchronization pattern start. Default value is 300 ms. 85 | W2 = 0x0F # 0x0-0xFFFF # ISO 9141 specific, max. time [ms] from the synchronization byte end to key byte 1 start. Default value is 20 ms. 86 | W3 = 0x10 # 0x0-0xFFFF # ISO 9141 specific, max. time [ms] between key byte 1 and key byte 2. Default value is 20 ms. 87 | W4 = 0x11 # 0x0-0xFFFF # ISO 9141 specific, 02.02-API: max. time [ms] between key byte 2 and its inversion from the tester. Default value is 50 ms. 88 | W5 = 0x12 # 0x0-0xFFFF # ISO 9141 specific, min. time [ms] before the tester begins retransmission of the address byte. Default value is 300 ms. 89 | TIDLE = 0x13 # 0x0-0xFFFF # ISO 9141 specific, bus idle time required before starting a fast initialization sequence. Default value is W5 value. 90 | TINL = 0x14 # 0x0-0xFFFF # ISO 9141 specific, the duration [ms] of the fast initialization low pulse. Default value is 25 ms. 91 | TWUP = 0x15 # 0x0-0xFFFF # ISO 9141 specific, the duration [ms] of the fast initialization wake-up pulse. Default value is 50 ms. 92 | PARITY = 0x16 # 0(NO_PARITY)/1(ODD_PARITY)/2(EVEN_PARITY) # ISO9141 specific, parity type for detecting bit errors. Default value is 0(NO_PARITY). 93 | BIT_SAMPLE_POINT = 0x17 # 0-100 # CAN specific, the desired bit sample point as a percentage of bit time. Default value is 80%. 94 | SYNCH_JUMP_WIDTH = 0x18 # 0-100 # CAN specific, the desired synchronization jump width as a percentage of the bit time. Default value is 15%. 95 | W0 = 0x19 96 | T1_MAX = 0x1A # 0x0-0xFFFF # SCI_X_XXXX specific, the max. interframe response delay. Default value is 20 ms. 97 | T2_MAX = 0x1B # 0x0-0xFFFF # SCI_X_XXXX specific, the max. interframe request delay.Default value is 100 ms. 98 | T4_MAX = 0x1C # 0x0-0xFFFF # SCI_X_XXXX specific, the max. intermessage response delay. Default value is 20 ms. 99 | T5_MAX = 0x1D # 0x0-0xFFFF # SCI_X_XXXX specific, the max. intermessage request delay. Default value is 100 ms. 100 | ISO15765_BS = 0x1E # 0x0-0xFF # ISO15765 specific, the block size for segmented transfers. 101 | ISO15765_STMIN = 0x1F # 0x0-0xFF # ISO15765 specific, the separation time for segmented transfers. 102 | DATA_BITS = 0x20 # 04.04-API only 103 | FIVE_BAUD_MOD = 0x21 104 | BS_TX = 0x22 105 | STMIN_TX = 0x23 106 | T3_MAX = 0x24 107 | ISO15765_WFT_MAX = 0x25 108 | 109 | 110 | class RxStatus(object): 111 | TX_MSG_TYPE = 1 112 | START_OF_MESSAGE = 2 113 | ISO15765_FIRST_FRAME = 2 114 | ISO15765_EXT_ADDR = 128 115 | RX_BREAK = 4 116 | TX_DONE = 8 117 | ISO15765_PADDING_ERROR = 16 118 | ISO15765_ADDR_TYPE = 128 119 | TX_INDICATION = 9 120 | 121 | 122 | class TxFlags(object): 123 | ISO15765_CAN_ID_29 = 0x00000140 124 | ISO15765_CAN_ID_11 = 0x00000040 125 | ISO15765_ADDR_TYPE = 0x00000080 126 | CAN_29BIT_ID = 0x00000100 127 | WAIT_P3_MIN_ONLY = 0x00000200 128 | SWCAN_HV_TX = 0x00000400 129 | SCI_MODE = 0x00400000 130 | SCI_MODE_TX_VOLTAGE = 0x00C00000 131 | SCI_TX_VOLTAGE = 0x00800000 132 | ISO15765_FRAME_PAD = 0x00000040 133 | TX_NORMAL_TRANSMIT = 0x00000000 134 | NONE = 0x00000000 135 | 136 | 137 | class Parameter(object): 138 | DATA_RATE = 0x01 139 | LOOPBACK = 0x03 140 | NODE_ADDRESS = 0x04 # Not supported 141 | NETWORK_LINE = 0x05 # Not supported 142 | P1_MIN = 0x06 # Don't use 143 | P1_MAX = 0x07 144 | P2_MIN = 0x08 # Don't use 145 | P2_MAX = 0x09 # Don't use 146 | P3_MIN = 0x0A 147 | P3_MAX = 0x0B # Don't use 148 | P4_MIN = 0x0C 149 | P4_MAX = 0x0D # Don't use 150 | # See W0 = = 0x19 151 | W1 = 0x0E 152 | W2 = 0x0F 153 | W3 = 0x10 154 | W4 = 0x11 155 | W5 = 0x12 156 | TIDLE = 0x13 157 | TINIL = 0x14 158 | TWUP = 0x15 159 | PARITY = 0x16 160 | 161 | 162 | class ParityEnumerate(object): 163 | NO_PARITY = 0 164 | ODD_PARITY = 1 165 | EVEN_PARITY = 2 166 | 167 | BIT_SAMPLE_POINT = 0x17 168 | SYNC_JUMP_WIDTH = 0x18 169 | W0 = 0x19 170 | T1_MAX = 0x1A # 0x0-0xFFFF # SCI_X_XXXX specific, the max. interframe response delay. Default value is 20 ms. 171 | T2_MAX = 0x1B # 0x0-0xFFFF # SCI_X_XXXX specific, the max. interframe request delay.Default value is 100 ms. 172 | T4_MAX = 0x1C # 0x0-0xFFFF # SCI_X_XXXX specific, the max. intermessage response delay. Default value is 20 ms. 173 | T5_MAX = 0x1D # 0x0-0xFFFF # SCI_X_XXXX specific, the max. intermessage request delay. Default value is 100 ms. 174 | ISO15765_BS = 0x1E 175 | ISO15765_STMIN = 0x1F 176 | DATA_BITS = 0x20 177 | FIVE_BAUD_MOD = 0x21 178 | BS_TX = 0x22 179 | STMIN_TX = 0x23 180 | T3_MAX = 0x24 181 | ISO15765_WFT_MAX = 0x25 182 | -------------------------------------------------------------------------------- /J2534/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 ecuunlock.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files J2534-Interface, to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /J2534/Registry.py: -------------------------------------------------------------------------------- 1 | import platform 2 | import winreg 3 | 4 | 5 | class ToolRegistryInfo: 6 | def __init__(self): 7 | if platform.architecture()[0] == "32bit": 8 | self.REG_PATH = r"Software\\PassThruSupport.04.04\\" 9 | else: 10 | self.REG_PATH = r"Software\\Wow6432Node\\PassThruSupport.04.04\\" 11 | 12 | # This protocol search list is only for j2534-1 13 | self.protocol_list = [ 14 | "CAN", 15 | "CAN channel", 16 | "ISO14230", 17 | "ISO15765", 18 | "ISO9141", 19 | "J1850PWM", 20 | "J1850VPW", 21 | "SCI_A_ENGINE", 22 | "SCI_A_TRANS", 23 | "SCI_B_ENGINE", 24 | "SCI_B_TRANS", 25 | ] 26 | 27 | self.tool_info = [] 28 | 29 | self.base_key = winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, self.REG_PATH) 30 | self.count = winreg.QueryInfoKey(self.base_key)[0] 31 | 32 | self.tool_list = [] 33 | self.j2534_registry_info = [] 34 | 35 | for i in range(self.count): 36 | device_key = winreg.OpenKeyEx(self.base_key, winreg.EnumKey(self.base_key, i)) 37 | name = winreg.QueryValueEx(device_key, "Name")[0] 38 | function_library = winreg.QueryValueEx(device_key, "FunctionLibrary")[0] 39 | vendor = winreg.QueryValueEx(device_key, "Vendor")[0] 40 | self.tool_list.append([name, function_library]) 41 | self.tool_info.append([i, vendor, name, function_library]) 42 | 43 | self.tool_info.extend(item for item in self.protocol_list if self.search_registry(item, device_key)) 44 | 45 | self.j2534_registry_info.append(self.tool_info) 46 | 47 | @staticmethod 48 | def search_registry(name, key): 49 | try: 50 | value, regtype = winreg.QueryValueEx(key, name) 51 | return value 52 | except WindowsError: 53 | return None 54 | 55 | def protocol_list(self): 56 | for n in self.j2534_registry_info: 57 | print(f'Registry info = {n}') 58 | 59 | def dll_path(self, index_of_tool): 60 | try: 61 | return self.j2534_registry_info[index_of_tool][0][3] 62 | except IndexError: 63 | return False 64 | 65 | def vendor(self, index_of_tool): 66 | try: 67 | return self.j2534_registry_info[index_of_tool][0][1] 68 | except IndexError: 69 | return False 70 | 71 | def name(self, index_of_tool): 72 | try: 73 | return self.j2534_registry_info[index_of_tool][0][2] 74 | except IndexError: 75 | return False 76 | 77 | def supported_protocols(self, index_of_tool): 78 | try: 79 | return self.j2534_registry_info[index_of_tool][1:] 80 | except IndexError: 81 | return False 82 | -------------------------------------------------------------------------------- /J2534/__init__.py: -------------------------------------------------------------------------------- 1 | from .Define import ProtocolID, BaudRate, TxFlags, FilterType, IoctlID, BaudRate, Parameter, Flags 2 | from .wrapper import J2534Api, j2534_api 3 | from .wrapper import PassThruMsgBuilder, PassThruMsg 4 | from .wrapper import pt_connect, pt_disconnect 5 | from .wrapper import pt_open, pt_close 6 | from .wrapper import pt_read_message, pt_write_message 7 | from .wrapper import pt_set_programming_voltage, pt_read_version, pt_get_last_error, pt_ioctl, pt_set_config 8 | from .wrapper import pt_start_message_filter, pt_stop_message_filter, pt_start_ecu_filter 9 | from .wrapper import pt_start_periodic_message, pt_stop_periodic_message 10 | from .wrapper import read_battery_volts, clear_transmit_buffer, clear_receive_buffer, clear_periodic_messages 11 | 12 | get_list_j2534_devices = j2534_api.get_devices 13 | set_j2534_device_to_connect = j2534_api.set_device 14 | -------------------------------------------------------------------------------- /J2534/dll.py: -------------------------------------------------------------------------------- 1 | import ctypes as ct 2 | 3 | PassThru_Data = (ct.c_ubyte * 4128) 4 | 5 | 6 | class PassThruMessageStructure(ct.Structure): 7 | _fields_ = [ 8 | ("ProtocolID", ct.c_ulong), 9 | ("RxStatus", ct.c_ulong), 10 | ("TxFlags", ct.c_ulong), 11 | ("Timestamp", ct.c_ulong), 12 | ("DataSize", ct.c_ulong), 13 | ("ExtraDataIndex", ct.c_ulong), 14 | ("Data", PassThru_Data)] 15 | 16 | 17 | class SetConfiguration(ct.Structure): 18 | _fields_ = [ 19 | ("Parameter", ct.c_ulong), 20 | ("Value", ct.c_ulong) 21 | ] 22 | 23 | def set_parameter(self, para): 24 | self.Parameter = para 25 | 26 | def set_value(self, para): 27 | self.Value = para 28 | 29 | 30 | class SetConfigurationList(ct.Structure): 31 | _fields_ = [ 32 | ("NumOfParams", ct.c_ulong), 33 | ("ConfigPtr", ct.POINTER(SetConfiguration)) 34 | ] 35 | 36 | 37 | def annotate(dll_object, function_name, argtypes, restype=None): 38 | function = getattr(dll_object._dll, function_name) 39 | function.argtypes = argtypes 40 | # restype is optional in the function_prototypes list 41 | if restype is None: 42 | restype = ct.c_long # dll_object.default_restype ## 43 | function.restype = restype 44 | setattr(dll_object, function_name, function) 45 | 46 | 47 | class MyDll(object): 48 | def __init__(self, ct_dll, **function_prototypes): 49 | self._dll = ct_dll 50 | for name, prototype in function_prototypes.items(): 51 | annotate(self, name, *prototype) 52 | 53 | 54 | class PassThruLibrary(MyDll): 55 | function_prototypes = { 56 | # extern "C" long WINAPI PassThruOpen (void *pName unsigned long *pDeviceID) 57 | 'PassThruOpen': [[ct.c_void_p, ct.POINTER(ct.c_ulong)]], 58 | # extern "C" long WINAPI PassThruClose (unsigned long DeviceID) 59 | 'PassThruClose': [[ct.c_ulong]], 60 | # extern "C" long WINAPI PassThruConnect (unsigned long DeviceID, unsigned long ProtocolID, unsigned long Flags, 61 | # unsigned long BaudRate, unsigned long *pChannelID) 62 | 'PassThruConnect': [[ct.c_ulong, ct.c_ulong, ct.c_ulong, ct.c_ulong, ct.POINTER(ct.c_ulong)]], 63 | # extern "C" long WINAPI PassThruDisconnect (unsigned long ChannelID) 64 | 'PassThruDisconnect': [[ct.c_ulong]], 65 | # extern "C" long WINAPI PassThruWriteMsgs (unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long 66 | # *pNumMsgs, unsigned long Timeout) 67 | 'PassThruReadMsgs': [[ct.c_ulong, ct.POINTER(PassThruMessageStructure), ct.POINTER(ct.c_ulong), ct.c_ulong]], 68 | # extern "C" long WINAPI PassThruWriteMsgs (unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long 69 | # *pNumMsgs, unsigned long Timeout) 70 | 'PassThruWriteMsgs': [[ct.c_ulong, ct.POINTER(PassThruMessageStructure), ct.POINTER(ct.c_ulong), ct.c_ulong]], 71 | # extern "C" long WINAPI PassThruStartPeriodicMsg (unsigned long ChannelID, PASSTHRU_MSG *pMsg, 72 | # unsigned long *pMsgID, unsigned long TimeInterval) 73 | 'PassThruStartPeriodicMsg': [ 74 | [ct.c_ulong, ct.POINTER(PassThruMessageStructure), ct.POINTER(ct.c_ulong), ct.c_ulong]], 75 | # extern "C" long WINAPI PassThruStopPeriodicMsg (unsigned long ChannelID, unsigned long MsgID) 76 | 'PassThruStopPeriodicMsg': [[ct.c_ulong, ct.c_ulong]], 77 | # extern "C" long WINAPI PassThruStartMsgFilter (unsigned long ChannelID, unsigned long FilterType, 78 | # PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID) 79 | 'PassThruStartMsgFilter': [ 80 | [ct.c_ulong, ct.c_ulong, ct.POINTER(PassThruMessageStructure), ct.POINTER(PassThruMessageStructure), 81 | ct.POINTER(None), 82 | ct.POINTER(ct.c_ulong)]], 83 | # extern "C" long WINAPI PassThruStopMsgFilter (unsigned long ChannelID, unsigned long FilterID) 84 | 'PassThruStopMsgFilter': [[ct.c_ulong, ct.c_ulong]], 85 | # extern "C" long WINAPI PassThruSetProgrammingVoltage (unsigned long DeviceID, unsigned long PinNumber, 86 | # unsigned long Voltage) 87 | 'PassThruSetProgrammingVoltage': [[ct.c_ulong, ct.c_ulong, ct.c_ulong]], 88 | # extern "C" long WINAPI PassThruReadVersion (unsigned long DeviceID, char *pFirmwareVersion, 89 | # char *pDllVersion, char *pApiVersion) 90 | 'PassThruReadVersion': [[ct.c_ulong, ct.c_char_p, ct.c_char_p, ct.c_char_p]], 91 | # extern "C" long WINAPI PassThruGetLastError (char *pErrorDescription) 92 | 'PassThruGetLastError': [[ct.c_char_p]], 93 | # extern "C" long WINAPI PassThruIoctl (unsigned long ChannelID, unsigned long IoctlID, 94 | # void *pInput, void *pOutput) 95 | 'PassThruIoctl': [[ct.c_ulong, ct.c_ulong, ct.c_void_p, ct.c_void_p]] 96 | } 97 | 98 | def __init__(self, ct_dll): 99 | # set default values for function_prototypes 100 | self.default_restype = ct.c_long 101 | super(PassThruLibrary, self).__init__(ct_dll, **self.function_prototypes) 102 | import ctypes as ct 103 | 104 | PassThru_Data = (ct.c_ubyte * 4128) 105 | 106 | 107 | class PassThruMessageStructure(ct.Structure): 108 | _fields_ = [ 109 | ("ProtocolID", ct.c_ulong), 110 | ("RxStatus", ct.c_ulong), 111 | ("TxFlags", ct.c_ulong), 112 | ("Timestamp", ct.c_ulong), 113 | ("DataSize", ct.c_ulong), 114 | ("ExtraDataIndex", ct.c_ulong), 115 | ("Data", PassThru_Data)] 116 | 117 | 118 | class SetConfiguration(ct.Structure): 119 | _fields_ = [ 120 | ("Parameter", ct.c_ulong), 121 | ("Value", ct.c_ulong) 122 | ] 123 | 124 | def set_parameter(self, para): 125 | self.Parameter = para 126 | 127 | def set_value(self, para): 128 | self.Value = para 129 | 130 | 131 | class SetConfigurationList(ct.Structure): 132 | _fields_ = [ 133 | ("NumOfParams", ct.c_ulong), 134 | ("ConfigPtr", ct.POINTER(SetConfiguration)) 135 | ] 136 | 137 | 138 | def annotate(dll_object, function_name, argtypes, restype=None): 139 | function = getattr(dll_object._dll, function_name) 140 | function.argtypes = argtypes 141 | # restype is optional in the function_prototypes list 142 | if restype is None: 143 | restype = ct.c_long # dll_object.default_restype ## 144 | function.restype = restype 145 | setattr(dll_object, function_name, function) 146 | 147 | 148 | class MyDll(object): 149 | def __init__(self, ct_dll, **function_prototypes): 150 | self._dll = ct_dll 151 | for name, prototype in function_prototypes.items(): 152 | annotate(self, name, *prototype) 153 | 154 | 155 | class PassThruLibrary(MyDll): 156 | function_prototypes = { 157 | # extern "C" long WINAPI PassThruOpen (void *pName unsigned long *pDeviceID) 158 | 'PassThruOpen': [[ct.c_void_p, ct.POINTER(ct.c_ulong)]], 159 | # extern "C" long WINAPI PassThruClose (unsigned long DeviceID) 160 | 'PassThruClose': [[ct.c_ulong]], 161 | # extern "C" long WINAPI PassThruConnect (unsigned long DeviceID, unsigned long ProtocolID, unsigned long Flags, 162 | # unsigned long BaudRate, unsigned long *pChannelID) 163 | 'PassThruConnect': [[ct.c_ulong, ct.c_ulong, ct.c_ulong, ct.c_ulong, ct.POINTER(ct.c_ulong)]], 164 | # extern "C" long WINAPI PassThruDisconnect (unsigned long ChannelID) 165 | 'PassThruDisconnect': [[ct.c_ulong]], 166 | # extern "C" long WINAPI PassThruWriteMsgs (unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long 167 | # *pNumMsgs, unsigned long Timeout) 168 | 'PassThruReadMsgs': [[ct.c_ulong, ct.POINTER(PassThruMessageStructure), ct.POINTER(ct.c_ulong), ct.c_ulong]], 169 | # extern "C" long WINAPI PassThruWriteMsgs (unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long 170 | # *pNumMsgs, unsigned long Timeout) 171 | 'PassThruWriteMsgs': [[ct.c_ulong, ct.POINTER(PassThruMessageStructure), ct.POINTER(ct.c_ulong), ct.c_ulong]], 172 | # extern "C" long WINAPI PassThruStartPeriodicMsg (unsigned long ChannelID, PASSTHRU_MSG *pMsg, 173 | # unsigned long *pMsgID, unsigned long TimeInterval) 174 | 'PassThruStartPeriodicMsg': [ 175 | [ct.c_ulong, ct.POINTER(PassThruMessageStructure), ct.POINTER(ct.c_ulong), ct.c_ulong]], 176 | # extern "C" long WINAPI PassThruStopPeriodicMsg (unsigned long ChannelID, unsigned long MsgID) 177 | 'PassThruStopPeriodicMsg': [[ct.c_ulong, ct.c_ulong]], 178 | # extern "C" long WINAPI PassThruStartMsgFilter (unsigned long ChannelID, unsigned long FilterType, 179 | # PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID) 180 | 'PassThruStartMsgFilter': [ 181 | [ct.c_ulong, ct.c_ulong, ct.POINTER(PassThruMessageStructure), ct.POINTER(PassThruMessageStructure), 182 | ct.POINTER(None), 183 | ct.POINTER(ct.c_ulong)]], 184 | # extern "C" long WINAPI PassThruStopMsgFilter (unsigned long ChannelID, unsigned long FilterID) 185 | 'PassThruStopMsgFilter': [[ct.c_ulong, ct.c_ulong]], 186 | # extern "C" long WINAPI PassThruSetProgrammingVoltage (unsigned long DeviceID, unsigned long PinNumber, 187 | # unsigned long Voltage) 188 | 'PassThruSetProgrammingVoltage': [[ct.c_ulong, ct.c_ulong, ct.c_ulong]], 189 | # extern "C" long WINAPI PassThruReadVersion (unsigned long DeviceID, char *pFirmwareVersion, 190 | # char *pDllVersion, char *pApiVersion) 191 | 'PassThruReadVersion': [[ct.c_ulong, ct.c_char_p, ct.c_char_p, ct.c_char_p]], 192 | # extern "C" long WINAPI PassThruGetLastError (char *pErrorDescription) 193 | 'PassThruGetLastError': [[ct.c_char_p]], 194 | # extern "C" long WINAPI PassThruIoctl (unsigned long ChannelID, unsigned long IoctlID, 195 | # void *pInput, void *pOutput) 196 | 'PassThruIoctl': [[ct.c_ulong, ct.c_ulong, ct.c_void_p, ct.c_void_p]] 197 | } 198 | 199 | def __init__(self, ct_dll): 200 | # set default values for function_prototypes 201 | self.default_restype = ct.c_long 202 | super(PassThruLibrary, self).__init__(ct_dll, **self.function_prototypes) 203 | import ctypes as ct 204 | 205 | PassThru_Data = (ct.c_ubyte * 4128) 206 | 207 | 208 | class PassThruMessageStructure(ct.Structure): 209 | _fields_ = [ 210 | ("ProtocolID", ct.c_ulong), 211 | ("RxStatus", ct.c_ulong), 212 | ("TxFlags", ct.c_ulong), 213 | ("Timestamp", ct.c_ulong), 214 | ("DataSize", ct.c_ulong), 215 | ("ExtraDataIndex", ct.c_ulong), 216 | ("Data", PassThru_Data)] 217 | 218 | 219 | class SetConfiguration(ct.Structure): 220 | _fields_ = [ 221 | ("Parameter", ct.c_ulong), 222 | ("Value", ct.c_ulong) 223 | ] 224 | 225 | def set_parameter(self, para): 226 | self.Parameter = para 227 | 228 | def set_value(self, para): 229 | self.Value = para 230 | 231 | 232 | class SetConfigurationList(ct.Structure): 233 | _fields_ = [ 234 | ("NumOfParams", ct.c_ulong), 235 | ("ConfigPtr", ct.POINTER(SetConfiguration)) 236 | ] 237 | 238 | 239 | def annotate(dll_object, function_name, argtypes, restype=None): 240 | function = getattr(dll_object._dll, function_name) 241 | function.argtypes = argtypes 242 | # restype is optional in the function_prototypes list 243 | if restype is None: 244 | restype = ct.c_long # dll_object.default_restype ## 245 | function.restype = restype 246 | setattr(dll_object, function_name, function) 247 | 248 | 249 | class MyDll(object): 250 | def __init__(self, ct_dll, **function_prototypes): 251 | self._dll = ct_dll 252 | for name, prototype in function_prototypes.items(): 253 | annotate(self, name, *prototype) 254 | 255 | 256 | class PassThruLibrary(MyDll): 257 | function_prototypes = { 258 | # extern "C" long WINAPI PassThruOpen (void *pName unsigned long *pDeviceID) 259 | 'PassThruOpen': [[ct.c_void_p, ct.POINTER(ct.c_ulong)]], 260 | # extern "C" long WINAPI PassThruClose (unsigned long DeviceID) 261 | 'PassThruClose': [[ct.c_ulong]], 262 | # extern "C" long WINAPI PassThruConnect (unsigned long DeviceID, unsigned long ProtocolID, unsigned long Flags, 263 | # unsigned long BaudRate, unsigned long *pChannelID) 264 | 'PassThruConnect': [[ct.c_ulong, ct.c_ulong, ct.c_ulong, ct.c_ulong, ct.POINTER(ct.c_ulong)]], 265 | # extern "C" long WINAPI PassThruDisconnect (unsigned long ChannelID) 266 | 'PassThruDisconnect': [[ct.c_ulong]], 267 | # extern "C" long WINAPI PassThruWriteMsgs (unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long 268 | # *pNumMsgs, unsigned long Timeout) 269 | 'PassThruReadMsgs': [[ct.c_ulong, ct.POINTER(PassThruMessageStructure), ct.POINTER(ct.c_ulong), ct.c_ulong]], 270 | # extern "C" long WINAPI PassThruWriteMsgs (unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long 271 | # *pNumMsgs, unsigned long Timeout) 272 | 'PassThruWriteMsgs': [[ct.c_ulong, ct.POINTER(PassThruMessageStructure), ct.POINTER(ct.c_ulong), ct.c_ulong]], 273 | # extern "C" long WINAPI PassThruStartPeriodicMsg (unsigned long ChannelID, PASSTHRU_MSG *pMsg, 274 | # unsigned long *pMsgID, unsigned long TimeInterval) 275 | 'PassThruStartPeriodicMsg': [ 276 | [ct.c_ulong, ct.POINTER(PassThruMessageStructure), ct.POINTER(ct.c_ulong), ct.c_ulong]], 277 | # extern "C" long WINAPI PassThruStopPeriodicMsg (unsigned long ChannelID, unsigned long MsgID) 278 | 'PassThruStopPeriodicMsg': [[ct.c_ulong, ct.c_ulong]], 279 | # extern "C" long WINAPI PassThruStartMsgFilter (unsigned long ChannelID, unsigned long FilterType, 280 | # PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID) 281 | 'PassThruStartMsgFilter': [ 282 | [ct.c_ulong, ct.c_ulong, ct.POINTER(PassThruMessageStructure), ct.POINTER(PassThruMessageStructure), 283 | ct.POINTER(None), 284 | ct.POINTER(ct.c_ulong)]], 285 | # extern "C" long WINAPI PassThruStopMsgFilter (unsigned long ChannelID, unsigned long FilterID) 286 | 'PassThruStopMsgFilter': [[ct.c_ulong, ct.c_ulong]], 287 | # extern "C" long WINAPI PassThruSetProgrammingVoltage (unsigned long DeviceID, unsigned long PinNumber, 288 | # unsigned long Voltage) 289 | 'PassThruSetProgrammingVoltage': [[ct.c_ulong, ct.c_ulong, ct.c_ulong]], 290 | # extern "C" long WINAPI PassThruReadVersion (unsigned long DeviceID, char *pFirmwareVersion, 291 | # char *pDllVersion, char *pApiVersion) 292 | 'PassThruReadVersion': [[ct.c_ulong, ct.c_char_p, ct.c_char_p, ct.c_char_p]], 293 | # extern "C" long WINAPI PassThruGetLastError (char *pErrorDescription) 294 | 'PassThruGetLastError': [[ct.c_char_p]], 295 | # extern "C" long WINAPI PassThruIoctl (unsigned long ChannelID, unsigned long IoctlID, 296 | # void *pInput, void *pOutput) 297 | 'PassThruIoctl': [[ct.c_ulong, ct.c_ulong, ct.c_void_p, ct.c_void_p]] 298 | } 299 | 300 | def __init__(self, ct_dll): 301 | # set default values for function_prototypes 302 | self.default_restype = ct.c_long 303 | super(PassThruLibrary, self).__init__(ct_dll, **self.function_prototypes) 304 | -------------------------------------------------------------------------------- /J2534/wrapper.py: -------------------------------------------------------------------------------- 1 | import ctypes as ct 2 | from typing import Any 3 | 4 | from .Define import IoctlID, Parameter 5 | from .Registry import ToolRegistryInfo 6 | from .dll import PassThru_Data, PassThruMessageStructure, \ 7 | SetConfigurationList, PassThruLibrary, SetConfiguration 8 | 9 | 10 | class MsgBuilder(PassThruMessageStructure): 11 | def build_transmit_data_block(self, data): 12 | self.DataSize = len(data) 13 | self.Data = PassThru_Data() 14 | for i in range(self.DataSize): 15 | self.Data[i] = data[i] 16 | 17 | def set_identifier(self, transmit_identifier): 18 | identifier = self.int_to_list(transmit_identifier) 19 | self.build_transmit_data_block(identifier) 20 | 21 | def set_identifier_and_data(self, _id, data=None): 22 | data = data or [] 23 | id_and_data = self.int_to_list(_id) + data 24 | self.build_transmit_data_block(id_and_data) 25 | 26 | @staticmethod 27 | def int_to_list(_id): 28 | if _id < 255: 29 | return [_id] 30 | return [_id >> 24 & 0xFF, _id >> 16 & 0xFF, _id >> 8 & 0xFF, _id & 0xFF] 31 | 32 | 33 | class PassThruMsgBuilder(MsgBuilder): # sets up the message structure 34 | def __init__(self, protocol_id, tx_flags, *args: Any, **kw: Any): 35 | super().__init__(*args, **kw) 36 | self.ProtocolID = protocol_id 37 | self.TxFlags = tx_flags 38 | 39 | def dump(self): 40 | print(f"ProtocolID = {str(self.ProtocolID)}") 41 | print(f"RxStatus = {str(self.RxStatus)}") 42 | print(f"TxFlags = {str(self.TxFlags)}") 43 | print(f"Timestamp = {str(self.Timestamp)}") 44 | print(f"DataSize = {str(self.DataSize)}") 45 | print(f"ExtraDataIndex = {str(self.ExtraDataIndex)}") 46 | print(self.build_hex_output()) 47 | 48 | def process_hex_output_line(self, line_start_index, line_end_index): 49 | line = "%04x | " % line_start_index 50 | for j in range(line_start_index, line_end_index): 51 | if j >= self.DataSize: 52 | break 53 | line += "%02X " % abs(self.Data[j]) 54 | line += " " * (3 * 16 + 7 - len(line)) + " | " 55 | for j in range(line_start_index, line_end_index): 56 | if j >= self.DataSize: 57 | break 58 | c = self.Data[j] if 0x20 <= self.Data[j] <= 0x7E else "." 59 | line += "%c" % c 60 | return line 61 | 62 | def build_hex_output(self): 63 | lines = [] 64 | for i in range(0, self.DataSize, 16): 65 | line_end_index = i + 16 66 | line = self.process_hex_output_line(i, line_end_index) 67 | lines.append(line) 68 | return "\n".join(lines) 69 | 70 | def dump_output(self): 71 | return "".join("%02X" % self.Data[j] for j in range(self.DataSize)) 72 | 73 | 74 | class PassThruMsg(PassThruMsgBuilder): # sets up the message structure 75 | pass 76 | 77 | 78 | class GetParameter(SetConfigurationList, Parameter): 79 | def __init__(self, *args: Any, **kw: Any): 80 | super().__init__(*args, **kw) 81 | self.number_of_parameters = len(Parameter.USED) 82 | self.parameters = SetConfiguration * self.number_of_parameters 83 | temporary_parameters = self.parameters() 84 | for i in range(self.number_of_parameters): 85 | temporary_parameters[i].set(Parameter.USED[i]) 86 | self.configuration_pointer = temporary_parameters 87 | 88 | 89 | class J2534Api: 90 | def __init__(self): 91 | self.pass_thru_library = None 92 | self.dll = None 93 | self.name = None 94 | tool_registry_info = ToolRegistryInfo() 95 | self._devices = tool_registry_info.tool_list 96 | 97 | def set_device(self, key=0): 98 | device = self._devices[key] 99 | self.name = device[0] 100 | self.dll = load_j2534_library(device[1]) 101 | self.pass_thru_library = PassThruLibrary(self.dll) 102 | 103 | def get_devices(self): 104 | return self._devices 105 | 106 | def __getattr__(self, name): 107 | try: 108 | return getattr(self.pass_thru_library, name) 109 | except AttributeError as e: 110 | raise AttributeError(f"{e} object has no attribute {name}") from e 111 | 112 | 113 | j2534_api = J2534Api() 114 | 115 | 116 | def load_j2534_library(dll_path=None): 117 | try: 118 | return ct.WinDLL(dll_path) 119 | except WindowsError: 120 | return False 121 | 122 | 123 | def pt_open(): 124 | device_id = ct.c_ulong() 125 | if j2534_api.PassThruOpen(ct.c_void_p(None), ct.byref(device_id)) != 0: 126 | return False 127 | return device_id.value 128 | 129 | 130 | def pt_close(device_id): 131 | return j2534_api.PassThruClose(device_id) 132 | 133 | 134 | def pt_connect(device_id, protocol_id, flags, baud_rate): 135 | channel_id = ct.c_ulong() 136 | if j2534_api.PassThruConnect(device_id, protocol_id, flags, baud_rate, ct.byref(channel_id)) != 0: 137 | return False 138 | return channel_id.value 139 | 140 | 141 | def pt_disconnect(channel_id): 142 | return j2534_api.PassThruDisconnect(channel_id) 143 | 144 | 145 | def pt_read_message(channel_id, messages, number_of_messages, message_timeout): 146 | return j2534_api.PassThruReadMsgs( 147 | channel_id, 148 | ct.byref(messages), 149 | ct.byref(ct.c_ulong(number_of_messages)), 150 | message_timeout, 151 | ) 152 | 153 | 154 | def pt_write_message(channel_id, messages, number_of_messages, message_timeout): 155 | return j2534_api.PassThruWriteMsgs( 156 | channel_id, 157 | ct.byref(messages), 158 | ct.byref(ct.c_ulong(number_of_messages)), 159 | message_timeout, 160 | ) 161 | 162 | 163 | def pt_start_periodic_message(channel_id, message_id, time_interval): 164 | periodic_id = ct.c_ulong() 165 | if j2534_api.PassThruStartPeriodicMsg(channel_id, ct.byref(message_id), ct.byref(periodic_id), time_interval) != 0: 166 | return False 167 | return periodic_id.value 168 | 169 | 170 | def pt_stop_periodic_message(channel_id, message_id): 171 | return j2534_api.PassThruStopPeriodicMsg(channel_id, message_id) 172 | 173 | 174 | def create_msg(protocol_id, tx_flag_0, mask_id, pattern_msgs, flow_control=None): 175 | if protocol_id in [ 176 | 1, 177 | 7, 178 | 8, 179 | 9, 180 | 10, 181 | ]: # check if using protocol j1850 or sci if so set pass filter... 182 | mask_message = PassThruMsg(protocol_id, tx_flag_0, 0) 183 | mask_message.set_identifier(mask_id) 184 | 185 | pattern_message = PassThruMsg(protocol_id, tx_flag_0, 0) 186 | pattern_message.set_identifier(pattern_msgs) 187 | else: 188 | mask_message = PassThruMsg(protocol_id, tx_flag_0, 0) 189 | mask_message.set_identifier_and_data(mask_id) 190 | 191 | pattern_message = PassThruMsg(protocol_id, tx_flag_0, 0) 192 | pattern_message.set_identifier_and_data(pattern_msgs) 193 | 194 | if flow_control is not None: 195 | flow_control_message = PassThruMsg(protocol_id, tx_flag_0, 0) 196 | flow_control_message.set_identifier_and_data(flow_control) 197 | return mask_message, pattern_message, flow_control_message 198 | return mask_message, pattern_message, None 199 | 200 | 201 | def pt_start_ecu_filter(channel_id, protocol_id, mask_id=None, pattern_msgs=None, flow_control=None, tx_flag_0=0): 202 | """start the msg filter""" 203 | filter_id = ct.c_ulong() 204 | 205 | if protocol_id in [6]: # check if using protocol ISO15765 if so set flow control filter... 206 | mask_message, pattern_message, flow_control_message = create_msg(protocol_id, tx_flag_0, mask_id, pattern_msgs, 207 | flow_control) 208 | 209 | if j2534_api.PassThruStartMsgFilter(channel_id, 3, ct.byref(mask_message), ct.byref(pattern_message), 210 | ct.byref(flow_control_message), ct.byref(filter_id)) != 0: 211 | return False 212 | return filter_id.value 213 | 214 | elif protocol_id in [ 215 | 1, 216 | 7, 217 | 8, 218 | 9, 219 | 10, 220 | ]: # check if using protocol j1850 or sci if so set pass filter... 221 | mask_message, pattern_message, _ = create_msg(protocol_id, 0, mask_id, pattern_msgs) 222 | 223 | if j2534_api.PassThruStartMsgFilter(channel_id, 1, ct.byref(mask_message), ct.byref(pattern_message), 224 | ct.c_void_p(None), ct.byref(filter_id)) != 0: 225 | return False 226 | return filter_id.value 227 | 228 | 229 | def pt_start_message_filter(channel_id, filter_type, mask_message, pattern_message, flow_control_message): 230 | filter_id = ct.c_ulong() 231 | if j2534_api.PassThruStartMsgFilter(channel_id, filter_type, ct.byref(mask_message), ct.byref(pattern_message), 232 | ct.byref(flow_control_message), ct.byref(filter_id)) != 0: 233 | return False 234 | return filter_id.value 235 | 236 | 237 | def pt_stop_message_filter(channel_id, filter_id): 238 | return j2534_api.PassThruStopMsgFilter(channel_id, filter_id) 239 | 240 | 241 | def pt_set_programming_voltage(device_id, pin_number, voltage): 242 | return j2534_api.PassThruSetProgrammingVoltage(device_id, pin_number, voltage) 243 | 244 | 245 | def pt_read_version(device_id): 246 | firmware_version = ct.create_string_buffer(80) 247 | dll_version = ct.create_string_buffer(80) 248 | api_version = ct.create_string_buffer(80) 249 | if j2534_api.PassThruReadVersion(device_id, firmware_version, dll_version, api_version) != 0: 250 | return ['error', 'error', 'error'] 251 | return [firmware_version.value.decode(), dll_version.value.decode(), api_version.value.decode()] 252 | 253 | 254 | def pt_get_last_error(): 255 | error_buffer = ct.create_string_buffer(80) 256 | j2534_api.PassThruGetLastError(error_buffer) 257 | return error_buffer.value 258 | 259 | 260 | def pt_ioctl(channel_id, ioctl_id, ioctl_input, output): 261 | return j2534_api.PassThruIoctl(channel_id, ioctl_id, ioctl_input, output) 262 | 263 | 264 | def pt_set_config(channel_id, parameters): 265 | conf = SetConfigurationList() 266 | conf.NumOfParams = len(parameters) 267 | elems = (SetConfiguration * len(parameters))() 268 | conf.ConfigPtr = ct.cast(elems, ct.POINTER(SetConfiguration)) 269 | for num in range(len(parameters)): 270 | conf.ConfigPtr[num].set_parameter(parameters[num][0]) 271 | conf.ConfigPtr[num].set_value(parameters[num][1]) 272 | ret = pt_ioctl(channel_id, IoctlID.SET_CONFIG, ct.byref(conf), ct.c_void_p(None)) 273 | return ret, conf.ConfigPtr 274 | 275 | 276 | def read_battery_volts(device_id): 277 | _voltage = ct.c_ulong() 278 | if pt_ioctl(device_id, IoctlID.READ_VBATT, ct.c_void_p(None), ct.byref(_voltage)) == 0: 279 | return _voltage.value / 1000.0 280 | return False 281 | 282 | 283 | def read_programming_voltage(channel_id): 284 | _voltage = ct.c_ulong() 285 | if pt_ioctl(channel_id, IoctlID.READ_PROG_VOLTAGE, ct.c_void_p(None), ct.byref(_voltage)) != 0: 286 | return False 287 | return _voltage.value / 1000.0 288 | 289 | 290 | def clear_transmit_buffer(channel_id): 291 | return pt_ioctl(channel_id, IoctlID.CLEAR_TX_BUFFER, ct.c_void_p(None), ct.c_void_p(None)) 292 | 293 | 294 | def clear_receive_buffer(channel_id): 295 | return pt_ioctl(channel_id, IoctlID.CLEAR_RX_BUFFER, ct.c_void_p(None), ct.c_void_p(None)) 296 | 297 | 298 | def clear_periodic_messages(channel_id): 299 | return pt_ioctl( 300 | channel_id, 301 | IoctlID.CLEAR_PERIODIC_MSGS, 302 | ct.c_void_p(None), 303 | ct.c_void_p(None), 304 | ) 305 | 306 | 307 | def clear_message_filters(channel_id): 308 | return pt_ioctl( 309 | channel_id, 310 | IoctlID.CLEAR_MSG_FILTERS, 311 | ct.c_void_p(None), 312 | ct.c_void_p(None), 313 | ) 314 | 315 | 316 | def clear_functional_message_lookup_table(channel_id): 317 | return pt_ioctl( 318 | channel_id, 319 | IoctlID.CLEAR_FUNCT_MSG_LOOKUP_TABLE, 320 | ct.c_void_p(None), 321 | ct.c_void_p(None), 322 | ) 323 | import ctypes as ct 324 | from typing import Any 325 | 326 | from .Define import IoctlID, Parameter 327 | from .Registry import ToolRegistryInfo 328 | from .dll import PassThru_Data, PassThruMessageStructure, \ 329 | SetConfigurationList, PassThruLibrary, SetConfiguration 330 | 331 | 332 | class MsgBuilder(PassThruMessageStructure): 333 | def build_transmit_data_block(self, data): 334 | self.DataSize = len(data) 335 | self.Data = PassThru_Data() 336 | for i in range(self.DataSize): 337 | self.Data[i] = data[i] 338 | 339 | def set_identifier(self, transmit_identifier): 340 | identifier = self.int_to_list(transmit_identifier) 341 | self.build_transmit_data_block(identifier) 342 | 343 | def set_identifier_and_data(self, _id, data=None): 344 | data = data or [] 345 | id_and_data = self.int_to_list(_id) + data 346 | self.build_transmit_data_block(id_and_data) 347 | 348 | @staticmethod 349 | def int_to_list(_id): 350 | if _id < 255: 351 | return [_id] 352 | return [_id >> 24 & 0xFF, _id >> 16 & 0xFF, _id >> 8 & 0xFF, _id & 0xFF] 353 | 354 | 355 | class PassThruMsgBuilder(MsgBuilder): # sets up the message structure 356 | def __init__(self, protocol_id, tx_flags, *args: Any, **kw: Any): 357 | super().__init__(*args, **kw) 358 | self.ProtocolID = protocol_id 359 | self.TxFlags = tx_flags 360 | 361 | def dump(self): 362 | print(f"ProtocolID = {str(self.ProtocolID)}") 363 | print(f"RxStatus = {str(self.RxStatus)}") 364 | print(f"TxFlags = {str(self.TxFlags)}") 365 | print(f"Timestamp = {str(self.Timestamp)}") 366 | print(f"DataSize = {str(self.DataSize)}") 367 | print(f"ExtraDataIndex = {str(self.ExtraDataIndex)}") 368 | print(self.build_hex_output()) 369 | 370 | def process_hex_output_line(self, line_start_index, line_end_index): 371 | line = "%04x | " % line_start_index 372 | for j in range(line_start_index, line_end_index): 373 | if j >= self.DataSize: 374 | break 375 | line += "%02X " % abs(self.Data[j]) 376 | line += " " * (3 * 16 + 7 - len(line)) + " | " 377 | for j in range(line_start_index, line_end_index): 378 | if j >= self.DataSize: 379 | break 380 | c = self.Data[j] if 0x20 <= self.Data[j] <= 0x7E else "." 381 | line += "%c" % c 382 | return line 383 | 384 | def build_hex_output(self): 385 | lines = [] 386 | for i in range(0, self.DataSize, 16): 387 | line_end_index = i + 16 388 | line = self.process_hex_output_line(i, line_end_index) 389 | lines.append(line) 390 | return "\n".join(lines) 391 | 392 | def dump_output(self): 393 | return "".join("%02X" % self.Data[j] for j in range(self.DataSize)) 394 | 395 | 396 | class PassThruMsg(PassThruMsgBuilder): # sets up the message structure 397 | pass 398 | 399 | 400 | class GetParameter(SetConfigurationList, Parameter): 401 | def __init__(self, *args: Any, **kw: Any): 402 | super().__init__(*args, **kw) 403 | self.number_of_parameters = len(Parameter.USED) 404 | self.parameters = SetConfiguration * self.number_of_parameters 405 | temporary_parameters = self.parameters() 406 | for i in range(self.number_of_parameters): 407 | temporary_parameters[i].set(Parameter.USED[i]) 408 | self.configuration_pointer = temporary_parameters 409 | 410 | 411 | class J2534Api: 412 | def __init__(self): 413 | self.pass_thru_library = None 414 | self.dll = None 415 | self.name = None 416 | tool_registry_info = ToolRegistryInfo() 417 | self._devices = tool_registry_info.tool_list 418 | 419 | def set_device(self, key=0): 420 | device = self._devices[key] 421 | self.name = device[0] 422 | self.dll = load_j2534_library(device[1]) 423 | self.pass_thru_library = PassThruLibrary(self.dll) 424 | 425 | def get_devices(self): 426 | return self._devices 427 | 428 | def __getattr__(self, name): 429 | try: 430 | return getattr(self.pass_thru_library, name) 431 | except AttributeError as e: 432 | raise AttributeError(f"{e} object has no attribute {name}") from e 433 | 434 | 435 | j2534_api = J2534Api() 436 | 437 | 438 | def load_j2534_library(dll_path=None): 439 | try: 440 | return ct.WinDLL(dll_path) 441 | except WindowsError: 442 | return False 443 | 444 | 445 | def pt_open(): 446 | device_id = ct.c_ulong() 447 | if j2534_api.PassThruOpen(ct.c_void_p(None), ct.byref(device_id)) != 0: 448 | return False 449 | return device_id.value 450 | 451 | 452 | def pt_close(device_id): 453 | return j2534_api.PassThruClose(device_id) 454 | 455 | 456 | def pt_connect(device_id, protocol_id, flags, baud_rate): 457 | channel_id = ct.c_ulong() 458 | if j2534_api.PassThruConnect(device_id, protocol_id, flags, baud_rate, ct.byref(channel_id)) != 0: 459 | return False 460 | return channel_id.value 461 | 462 | 463 | def pt_disconnect(channel_id): 464 | return j2534_api.PassThruDisconnect(channel_id) 465 | 466 | 467 | def pt_read_message(channel_id, messages, number_of_messages, message_timeout): 468 | return j2534_api.PassThruReadMsgs( 469 | channel_id, 470 | ct.byref(messages), 471 | ct.byref(ct.c_ulong(number_of_messages)), 472 | message_timeout, 473 | ) 474 | 475 | 476 | def pt_write_message(channel_id, messages, number_of_messages, message_timeout): 477 | return j2534_api.PassThruWriteMsgs( 478 | channel_id, 479 | ct.byref(messages), 480 | ct.byref(ct.c_ulong(number_of_messages)), 481 | message_timeout, 482 | ) 483 | 484 | 485 | def pt_start_periodic_message(channel_id, message_id, time_interval): 486 | periodic_id = ct.c_ulong() 487 | if j2534_api.PassThruStartPeriodicMsg(channel_id, ct.byref(message_id), ct.byref(periodic_id), time_interval) != 0: 488 | return False 489 | return periodic_id.value 490 | 491 | 492 | def pt_stop_periodic_message(channel_id, message_id): 493 | return j2534_api.PassThruStopPeriodicMsg(channel_id, message_id) 494 | 495 | 496 | def create_msg(protocol_id, tx_flag_0, mask_id, pattern_msgs, flow_control=None): 497 | if protocol_id in [ 498 | 1, 499 | 7, 500 | 8, 501 | 9, 502 | 10, 503 | ]: # check if using protocol j1850 or sci if so set pass filter... 504 | mask_message = PassThruMsg(protocol_id, tx_flag_0, 0) 505 | mask_message.set_identifier(mask_id) 506 | 507 | pattern_message = PassThruMsg(protocol_id, tx_flag_0, 0) 508 | pattern_message.set_identifier(pattern_msgs) 509 | else: 510 | mask_message = PassThruMsg(protocol_id, tx_flag_0, 0) 511 | mask_message.set_identifier_and_data(mask_id) 512 | 513 | pattern_message = PassThruMsg(protocol_id, tx_flag_0, 0) 514 | pattern_message.set_identifier_and_data(pattern_msgs) 515 | 516 | if flow_control is not None: 517 | flow_control_message = PassThruMsg(protocol_id, tx_flag_0, 0) 518 | flow_control_message.set_identifier_and_data(flow_control) 519 | return mask_message, pattern_message, flow_control_message 520 | return mask_message, pattern_message, None 521 | 522 | 523 | def pt_start_ecu_filter(channel_id, protocol_id, mask_id=None, pattern_msgs=None, flow_control=None, tx_flag_0=0): 524 | """start the msg filter""" 525 | filter_id = ct.c_ulong() 526 | 527 | if protocol_id in [6]: # check if using protocol ISO15765 if so set flow control filter... 528 | mask_message, pattern_message, flow_control_message = create_msg(protocol_id, tx_flag_0, mask_id, pattern_msgs, 529 | flow_control) 530 | 531 | if j2534_api.PassThruStartMsgFilter(channel_id, 3, ct.byref(mask_message), ct.byref(pattern_message), 532 | ct.byref(flow_control_message), ct.byref(filter_id)) != 0: 533 | return False 534 | return filter_id.value 535 | 536 | elif protocol_id in [ 537 | 1, 538 | 7, 539 | 8, 540 | 9, 541 | 10, 542 | ]: # check if using protocol j1850 or sci if so set pass filter... 543 | mask_message, pattern_message, _ = create_msg(protocol_id, 0, mask_id, pattern_msgs) 544 | 545 | if j2534_api.PassThruStartMsgFilter(channel_id, 1, ct.byref(mask_message), ct.byref(pattern_message), 546 | ct.c_void_p(None), ct.byref(filter_id)) != 0: 547 | return False 548 | return filter_id.value 549 | 550 | 551 | def pt_start_message_filter(channel_id, filter_type, mask_message, pattern_message, flow_control_message): 552 | filter_id = ct.c_ulong() 553 | if j2534_api.PassThruStartMsgFilter(channel_id, filter_type, ct.byref(mask_message), ct.byref(pattern_message), 554 | ct.byref(flow_control_message), ct.byref(filter_id)) != 0: 555 | return False 556 | return filter_id.value 557 | 558 | 559 | def pt_stop_message_filter(channel_id, filter_id): 560 | return j2534_api.PassThruStopMsgFilter(channel_id, filter_id) 561 | 562 | 563 | def pt_set_programming_voltage(device_id, pin_number, voltage): 564 | return j2534_api.PassThruSetProgrammingVoltage(device_id, pin_number, voltage) 565 | 566 | 567 | def pt_read_version(device_id): 568 | firmware_version = ct.create_string_buffer(80) 569 | dll_version = ct.create_string_buffer(80) 570 | api_version = ct.create_string_buffer(80) 571 | if j2534_api.PassThruReadVersion(device_id, firmware_version, dll_version, api_version) != 0: 572 | return ['error', 'error', 'error'] 573 | return [firmware_version.value.decode(), dll_version.value.decode(), api_version.value.decode()] 574 | 575 | 576 | def pt_get_last_error(): 577 | error_buffer = ct.create_string_buffer(80) 578 | j2534_api.PassThruGetLastError(error_buffer) 579 | return error_buffer.value 580 | 581 | 582 | def pt_ioctl(channel_id, ioctl_id, ioctl_input, output): 583 | return j2534_api.PassThruIoctl(channel_id, ioctl_id, ioctl_input, output) 584 | 585 | 586 | def pt_set_config(channel_id, parameters): 587 | conf = SetConfigurationList() 588 | conf.NumOfParams = len(parameters) 589 | elems = (SetConfiguration * len(parameters))() 590 | conf.ConfigPtr = ct.cast(elems, ct.POINTER(SetConfiguration)) 591 | for num in range(len(parameters)): 592 | conf.ConfigPtr[num].set_parameter(parameters[num][0]) 593 | conf.ConfigPtr[num].set_value(parameters[num][1]) 594 | ret = pt_ioctl(channel_id, IoctlID.SET_CONFIG, ct.byref(conf), ct.c_void_p(None)) 595 | return ret, conf.ConfigPtr 596 | 597 | 598 | def read_battery_volts(device_id): 599 | _voltage = ct.c_ulong() 600 | if pt_ioctl(device_id, IoctlID.READ_VBATT, ct.c_void_p(None), ct.byref(_voltage)) == 0: 601 | return _voltage.value / 1000.0 602 | return False 603 | 604 | 605 | def read_programming_voltage(channel_id): 606 | _voltage = ct.c_ulong() 607 | if pt_ioctl(channel_id, IoctlID.READ_PROG_VOLTAGE, ct.c_void_p(None), ct.byref(_voltage)) != 0: 608 | return False 609 | return _voltage.value / 1000.0 610 | 611 | 612 | def clear_transmit_buffer(channel_id): 613 | return pt_ioctl(channel_id, IoctlID.CLEAR_TX_BUFFER, ct.c_void_p(None), ct.c_void_p(None)) 614 | 615 | 616 | def clear_receive_buffer(channel_id): 617 | return pt_ioctl(channel_id, IoctlID.CLEAR_RX_BUFFER, ct.c_void_p(None), ct.c_void_p(None)) 618 | 619 | 620 | def clear_periodic_messages(channel_id): 621 | return pt_ioctl( 622 | channel_id, 623 | IoctlID.CLEAR_PERIODIC_MSGS, 624 | ct.c_void_p(None), 625 | ct.c_void_p(None), 626 | ) 627 | 628 | 629 | def clear_message_filters(channel_id): 630 | return pt_ioctl( 631 | channel_id, 632 | IoctlID.CLEAR_MSG_FILTERS, 633 | ct.c_void_p(None), 634 | ct.c_void_p(None), 635 | ) 636 | 637 | 638 | def clear_functional_message_lookup_table(channel_id): 639 | return pt_ioctl( 640 | channel_id, 641 | IoctlID.CLEAR_FUNCT_MSG_LOOKUP_TABLE, 642 | ct.c_void_p(None), 643 | ct.c_void_p(None), 644 | ) 645 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Python J2534/AutoComm 2 | This is a automatic interface to the J2534 API written in Python 3.10 32bit 3 | ... 4 | 5 | ## Automatically find j2534 device 6 | 7 | from AutoJ2534.Interface import j2534_communication 8 | 9 | tool_info = j2534_communication.auto_connect() 10 | 11 | print(tool_info) 12 | 13 | ... 14 | 15 | 16 | ## Can Bus Transmit and receive 17 | 18 | data = j2534_communication.transmit_and_receive_message([0x1a, 0x90]) 19 | 20 | print(data) 21 | 22 | ... 23 | 24 | 25 | --------------------------------------------------------------------------------