Skip to content

Commit c1c5b03

Browse files
committed
uds lib
1 parent 162f485 commit c1c5b03

File tree

1 file changed

+363
-0
lines changed

1 file changed

+363
-0
lines changed

python/uds.py

+363
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
#!/usr/bin/env python
2+
import time
3+
import struct
4+
from enum import Enum
5+
import threading
6+
7+
# class Services(Enum):
8+
# DiagnosticSessionControl = 0x10
9+
# ECUReset = 0x11
10+
# SecurityAccess = 0x27
11+
# CommunicationControl = 0x28
12+
# TesterPresent = 0x3E
13+
# AccessTimingParameter = 0x83
14+
# SecuredDataTransmission = 0x84
15+
# ControlDTCSetting = 0x85
16+
# ResponseOnEvent = 0x86
17+
# LinkControl = 0x87
18+
# ReadDataByIdentifier = 0x22
19+
# ReadMemoryByAddress = 0x23
20+
# ReadScalingDataByIdentifier = 0x24
21+
# ReadDataByPeriodicIdentifier = 0x2A
22+
# DynamicallyDefineDataIdentifier = 0x2C
23+
# WriteDataByIdentifier = 0x2E
24+
# WriteMemoryByAddress = 0x3D
25+
# ClearDiagnosticInformation = 0x14
26+
# ReadDTCInformation = 0x19
27+
# InputOutputControlByIdentifier = 0x2F
28+
# RoutineControl = 0x31
29+
# RequestDownload = 0x34
30+
# RequestUpload = 0x35
31+
# TransferData = 0x36
32+
# RequestTransferExit = 0x37
33+
34+
_negative_response_codes = {
35+
'\x00': 'positive response',
36+
'\x10': 'general reject',
37+
'\x11': 'service not supported',
38+
'\x12': 'sub-function not supported',
39+
'\x13': 'incorrect message length or invalid format',
40+
'\x14': 'response too long',
41+
'\x21': 'busy repeat request',
42+
'\x22': 'conditions not correct',
43+
'\x24': 'request sequence error',
44+
'\x25': 'no response from subnet component',
45+
'\x26': 'failure prevents execution of requested action',
46+
'\x31': 'request out of range',
47+
'\x33': 'security access denied',
48+
'\x35': 'invalid key',
49+
'\x36': 'exceed numebr of attempts',
50+
'\x37': 'required time delay not expired',
51+
'\x70': 'upload download not accepted',
52+
'\x71': 'transfer data suspended',
53+
'\x72': 'general programming failure',
54+
'\x73': 'wrong block sequence counter',
55+
'\x78': 'request correctly received - response pending',
56+
'\x7e': 'sub-function not supported in active session',
57+
'\x7f': 'service not supported in active session',
58+
'\x81': 'rpm too high',
59+
'\x82': 'rpm too low',
60+
'\x83': 'engine is running',
61+
'\x84': 'engine is not running',
62+
'\x85': 'engine run time too low',
63+
'\x86': 'temperature too high',
64+
'\x87': 'temperature too low',
65+
'\x88': 'vehicle speed too high',
66+
'\x89': 'vehicle speed too low',
67+
'\x8a': 'throttle/pedal too high',
68+
'\x8b': 'throttle/pedal too low',
69+
'\x8c': 'transmission not in neutral',
70+
'\x8d': 'transmission not in gear',
71+
'\x8f': 'brake switch(es) not closed',
72+
'\x90': 'shifter lever not in park',
73+
'\x91': 'torque converter clutch locked',
74+
'\x92': 'voltage too high',
75+
'\x93': 'voltage too low',
76+
}
77+
78+
# generic uds request
79+
def _request(address, service, subfunction, data=None):
80+
# TODO: send request
81+
# TODO: wait for response
82+
83+
# raise exception on error
84+
if resp[0] == '\x7f'
85+
error_code = resp[2]
86+
error_desc = _negative_response_codes[error_code]
87+
raise Exception('[{}] {}'.format(hex(ord(error_code)), error_desc))
88+
89+
resp_sid = ord(resp[0]) if len(resp) > 0 else None
90+
if service != resp_sid + 0x40:
91+
resp_sid_hex = hex(resp_sid) if resp_sid is not None else None
92+
raise Exception('invalid response service id: {}'.format(resp_sid_hex))
93+
94+
if subfunction is None:
95+
resp_subf = ord(resp[1]) if len(resp) > 1 else None
96+
if subfunction != resp_subf:
97+
resp_subf_hex = hex(resp_subf) if resp_subf is not None else None
98+
raise Exception('invalid response subfunction: {}'.format(hex(resp_subf)))
99+
100+
# return data (exclude service id and sub-function id)
101+
return resp[(1 if subfunction is None else 2):]
102+
103+
# services
104+
class DIAGNOSTIC_SESSION_CONTROL_TYPE(Enum):
105+
DEFAULT = 1
106+
PROGRAMMING = 2
107+
EXTENDED_DIAGNOSTIC = 3
108+
SAFETY_SYSTEM_DIAGNOSTIC = 4
109+
110+
def diagnostic_session_control(address, session_type):
111+
_request(address, service=0x10, subfunction=session_type)
112+
113+
class ECU_RESET_TYPE(Enum):
114+
HARD = 1
115+
KEY_OFF_ON = 2
116+
SOFT = 3
117+
ENABLE_RAPID_POWER_SHUTDOWN = 4
118+
DISABLE_RAPID_POWER_SHUTDOWN = 5
119+
120+
def ecu_reset(address, reset_type):
121+
resp = _request(address, service=0x11, subfunction=reset_type)
122+
power_down_time = None
123+
if reset_type == RESET_TYPE.ENABLE_RAPID_POWER_SHUTDOWN
124+
power_down_time = ord(resp[0])
125+
return {"power_down_time": power_down_time}
126+
127+
class SECURITY_ACCESS_TYPE(Enum):
128+
REQUEST_SEED = 1
129+
SEND_KEY = 2
130+
131+
def security_access(address, access_type, security_key=None):
132+
request_seed = access_type % 2 == 0
133+
if request_seed and security_key is not None:
134+
raise ValueError('security_key not allowed')
135+
if not request_seed and security_key is None:
136+
raise ValueError('security_key is missing')
137+
resp = _request(address, service=0x27, subfunction=access_type, data=security_key)
138+
if request_seed:
139+
return {"security_seed": resp}
140+
141+
class COMMUNICATION_CONTROL_TYPE(Enum):
142+
ENABLE_RX_ENABLE_TX = 0
143+
ENABLE_RX_DISABLE_TX = 1
144+
DISABLE_RX_ENABLE_TX = 2
145+
DISABLE_RX_DISABLE_TX = 3
146+
147+
class COMMUNICATION_CONTROL_MESSAGE_TYPE(Enum):
148+
NORMAL = 1
149+
NETWORK_MANAGEMENT = 2
150+
NORMAL_AND_NETWORK_MANAGEMENT = 3
151+
152+
def communication_control(address, control_type, message_type):
153+
data = chr(message_type)
154+
_request(address, service=0x28, subfunction=control_type, data=data)
155+
156+
def tester_present(address):
157+
_request(address, service=0x3E, subfunction=0x00)
158+
159+
class ACCESS_TIMING_PARAMETER_TYPE(Enum):
160+
READ_EXTENDED_SET = 1
161+
SET_TO_DEFAULT_VALUES = 2
162+
READ_CURRENTLY_ACTIVE = 3
163+
SET_TO_GIVEN_VALUES = 4
164+
165+
def access_timing_parameter(address, parameter_type, parameter_values):
166+
write_custom_values = parameter_type == ACCESS_TIMING_PARAMETER_TYPE.SET_TO_GIVEN_VALUES
167+
read_values = (
168+
parameter_type == ACCESS_TIMING_PARAMETER_TYPE.READ_CURRENTLY_ACTIVE or
169+
parameter_type == ACCESS_TIMING_PARAMETER_TYPE.READ_EXTENDED_SET
170+
)
171+
if not write_custom_values and parameter_values is not None:
172+
raise ValueError('parameter_values not allowed')
173+
if write_custom_values and parameter_values is None:
174+
raise ValueError('parameter_values is missing')
175+
resp = _request(address, service=0x83, subfunction=parameter_type, data=parameter_values)
176+
if read_values:
177+
# TODO: parse response into values?
178+
return {"parameter_values": resp}
179+
180+
def secured_data_transmission(address, data):
181+
# TODO: split data into multiple input parameters?
182+
resp = _request(address, service=0x84, subfunction=None, data=data)
183+
# TODO: parse response into multiple output values?
184+
return {"data"=resp}
185+
186+
class CONTROL_DTC_SETTING_TYPE(Enum):
187+
ON = 1
188+
OFF = 2
189+
190+
def control_dtc_setting(address, setting_type):
191+
_request(address, service=0x85, subfunction=setting_type)
192+
193+
class RESPONSE_ON_EVENT_TYPE(Enum):
194+
STOP_RESPONSE_ON_EVENT = 0
195+
ON_DTC_STATUS_CHANGE = 1
196+
ON_TIMER_INTERRUPT = 2
197+
ON_CHANGE_OF_DATA_IDENTIFIER = 3
198+
REPORT_ACTIVATED_EVENTS = 4
199+
START_RESPONSE_ON_EVENT = 5
200+
CLEAR_RESPONSE_ON_EVENT = 6
201+
ON_COMPARISON_OF_VALUES = 7
202+
203+
def response_on_event(address, event_type, store_event, window_time, event_type_record, service_response_record):
204+
if store_event:
205+
event_type |= 0x20
206+
# TODO: split record parameters into arrays
207+
data = char(window_time) + event_type_record + service_response_record
208+
resp = _request(address, service=0x86, subfunction=event_type, data=data)
209+
# TODO: parse the reset of response
210+
211+
class LINK_CONTROL_TYPE(Enum):
212+
VERIFY_BAUDRATE_TRANSITION_WITH_FIXED_BAUDRATE = 1
213+
VERIFY_BAUDRATE_TRANSITION_WITH_SPECIFIC_BAUDRATE = 2
214+
TRANSITION_BAUDRATE = 3
215+
216+
class LINK_CONTROL_BAUD_RATE(Enum):
217+
PC9600 = 1
218+
PC19200 = 2
219+
PC38400 = 3
220+
PC57600 = 4
221+
PC115200 = 5
222+
CAN125000 = 16
223+
CAN250000 = 17
224+
CAN500000 = 18
225+
CAN1000000 = 19
226+
227+
def link_control(address, control_type, baud_rate=None):
228+
if LINK_CONTROL_TYPE.VERIFY_BAUDRATE_TRANSITION_WITH_FIXED_BAUDRATE:
229+
# baud_rate = LINK_CONTROL_BAUD_RATE
230+
data = chr(baud_rate)
231+
elif LINK_CONTROL_TYPE.VERIFY_BAUDRATE_TRANSITION_WITH_SPECIFIC_BAUDRATE:
232+
# baud_rate = custom value (3 bytes big-endian)
233+
data = struct.pack('!I', baud_rate)[1:]
234+
else:
235+
data = None
236+
_request(address, service=0x87, subfunction=control_type, data=data)
237+
238+
class DATA_IDENTIFIER(Enum):
239+
BOOT_SOFTWARE_IDENTIFICATION = 0XF180
240+
APPLICATION_SOFTWARE_IDENTIFICATION = 0XF181
241+
APPLICATION_DATA_IDENTIFICATION = 0XF182
242+
BOOT_SOFTWARE_FINGERPRINT = 0XF183
243+
APPLICATION_SOFTWARE_FINGERPRINT = 0XF184
244+
APPLICATION_DATA_FINGERPRINT = 0XF185
245+
ACTIVE_DIAGNOSTIC_SESSION = 0XF186
246+
VEHICLE_MANUFACTURER_SPARE_PART_NUMBER = 0XF187
247+
VEHICLE_MANUFACTURER_ECU_SOFTWARE_NUMBER = 0XF188
248+
VEHICLE_MANUFACTURER_ECU_SOFTWARE_VERSION_NUMBER = 0XF189
249+
SYSTEM_SUPPLIER_IDENTIFIER = 0XF18A
250+
ECU_MANUFACTURING_DATE = 0XF18B
251+
ECU_SERIAL_NUMBER = 0XF18C
252+
SUPPORTED_FUNCTIONAL_UNITS = 0XF18D
253+
VEHICLE_MANUFACTURER_KIT_ASSEMBLY_PART_NUMBER = 0XF18E
254+
VIN = 0XF190
255+
VEHICLE_MANUFACTURER_ECU_HARDWARE_NUMBER = 0XF191
256+
SYSTEM_SUPPLIER_ECU_HARDWARE_NUMBER = 0XF192
257+
SYSTEM_SUPPLIER_ECU_HARDWARE_VERSION_NUMBER = 0XF193
258+
SYSTEM_SUPPLIER_ECU_SOFTWARE_NUMBER = 0XF194
259+
SYSTEM_SUPPLIER_ECU_SOFTWARE_VERSION_NUMBER = 0XF195
260+
EXHAUST_REGULATION_OR_TYPE_APPROVAL_NUMBER = 0XF196
261+
SYSTEM_NAME_OR_ENGINE_TYPE = 0XF197
262+
REPAIR_SHOP_CODE_OR_TESTER_SERIAL_NUMBER = 0XF198
263+
PROGRAMMING_DATE = 0XF199
264+
CALIBRATION_REPAIR_SHOP_CODE_OR_CALIBRATION_EQUIPMENT_SERIAL_NUMBER = 0XF19A
265+
CALIBRATION_DATE = 0XF19B
266+
CALIBRATION_EQUIPMENT_SOFTWARE_NUMBER = 0XF19C
267+
ECU_INSTALLATION_DATE = 0XF19D
268+
ODX_FILE = 0XF19E
269+
ENTITY = 0XF19F
270+
271+
def read_data_by_identifier(address, data_identifier):
272+
# TODO: support list of identifiers
273+
data = struct.pack('!H', data_id)
274+
resp = _request(address, service=0x22, subfunction=None, data=data)
275+
resp_id = struct.unpack('!H', data[0:2])[0] if len(data) >= 2 else None
276+
if resp_id != data_id:
277+
raise ValueError('invalid response data identifier: {}'.format(hex(resp_id)))
278+
return resp[2:]
279+
280+
def read_memory_by_address(address, memory_address, memory_size, memory_address_bytes=4, memory_size_bytes=4):
281+
if memory_address_bytes < 1 or memory_address_bytes > 4:
282+
raise ValueError('invalid memory_address_bytes: {}'.format(memory_address_bytes))
283+
if memory_size_bytes < 1 or memory_size_bytes > 4:
284+
raise ValueError('invalid memory_size_bytes: {}'.format(memory_size_bytes))
285+
data = struct.pack('!BB', memory_size_bytes, memory_address_bytes)
286+
287+
if memory_address >= 1<<(memory_address_bytes*8)
288+
raise ValueError('invalid memory_address: {}'.format(memory_address))
289+
data += struct.pack('!I', memory_address)[4-memory_address_bytes:]
290+
if memory_size >= 1<<(memory_size_bytes*8)
291+
raise ValueError('invalid memory_size: {}'.format(memory_address))
292+
data += struct.pack('!I', memory_size)[4-memory_size_bytes:]
293+
294+
resp = _request(address, service=0x23, subfunction=None, data=data)
295+
return resp
296+
297+
def read_scaling_data_by_identifier(address):
298+
raise NotImplementedError()
299+
_request(address, service=0x24, subfunction=0x00)
300+
301+
def read_data_by_periodic_identifier(address):
302+
raise NotImplementedError()
303+
_request(address, service=0x2A, subfunction=0x00)
304+
305+
def dynamically_define_data_identifier(address):
306+
raise NotImplementedError()
307+
_request(address, service=0x2C, subfunction=0x00)
308+
309+
def write_data_by_identifier(address):
310+
raise NotImplementedError()
311+
_request(address, service=0x2E, subfunction=0x00)
312+
313+
def write_memory_by_address(address):
314+
raise NotImplementedError()
315+
_request(address, service=0x3D, subfunction=0x00)
316+
317+
def clear_diagnostic_information(address):
318+
raise NotImplementedError()
319+
_request(address, service=0x14, subfunction=0x00)
320+
321+
def read_dtc_information(address):
322+
raise NotImplementedError()
323+
_request(address, service=0x19, subfunction=0x00)
324+
325+
class INPUT_OUTPUT_CONTROL_PARAMETER(Enum):
326+
RETURN_CONTROL_TO_ECU = 0
327+
RESET_TO_DEFAULT = 1
328+
FREEZE_CURRENT_STATE = 2
329+
SHORT_TERM_ADJUSTMENT = 3
330+
331+
def input_output_control_by_identifier(address):
332+
raise NotImplementedError()
333+
_request(address, service=0x2F, subfunction=0x00)
334+
335+
class ROUTINE_CONTROL_TYPE(Enum):
336+
ERASE_MEMORY = 0xFF00
337+
CHECK_PROGRAMMING_DEPENDENCIES = 0xFF01
338+
ERASE_MIRROR_MEMORY_DTCS = 0xFF02
339+
340+
def routine_control(address):
341+
raise NotImplementedError()
342+
_request(address, service=0x31, subfunction=0x00)
343+
344+
def request_download(address):
345+
raise NotImplementedError()
346+
_request(address, service=0x34, subfunction=0x00)
347+
348+
def request_upload(address):
349+
raise NotImplementedError()
350+
_request(address, service=0x35, subfunction=0x00)
351+
352+
def transfer_data(address):
353+
raise NotImplementedError()
354+
_request(address, service=0x36, subfunction=0x00)
355+
356+
def request_transfer_exit(address)
357+
raise NotImplementedError()
358+
_request(address, service=0x37, subfunction=0x00)
359+
360+
if __name__ == "__main__":
361+
from . import uds
362+
# examples
363+
vin = uds.read_data_by_identifier(0x18da10f1, uds.DATA_IDENTIFIER.VIN)

0 commit comments

Comments
 (0)