-
Notifications
You must be signed in to change notification settings - Fork 9.3k
/
Copy pathvw_mqb_config.py
executable file
·160 lines (140 loc) · 7.77 KB
/
vw_mqb_config.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#!/usr/bin/env python3
import argparse
import struct
from enum import IntEnum
from opendbc.car.uds import UdsClient, MessageTimeoutError, NegativeResponseError, SESSION_TYPE,\
DATA_IDENTIFIER_TYPE, ACCESS_TYPE
from panda import Panda
from datetime import date
# TODO: extend UDS library to allow custom/vendor-defined data identifiers without ignoring type checks
class VOLKSWAGEN_DATA_IDENTIFIER_TYPE(IntEnum):
CODING = 0x0600
# TODO: extend UDS library security_access() to take an access level offset per ISO 14229-1:2020 10.4 and remove this
class ACCESS_TYPE_LEVEL_1(IntEnum):
REQUEST_SEED = ACCESS_TYPE.REQUEST_SEED + 2
SEND_KEY = ACCESS_TYPE.SEND_KEY + 2
MQB_EPS_CAN_ADDR = 0x712
RX_OFFSET = 0x6a
if __name__ == "__main__":
desc_text = "Shows Volkswagen EPS software and coding info, and enables or disables Heading Control Assist " + \
"(Lane Assist). Useful for enabling HCA on cars without factory Lane Assist that want to use " + \
"openpilot integrated at the CAN gateway (J533)."
epilog_text = "This tool is meant to run directly on a vehicle-installed comma three, with the " + \
"openpilot/tmux processes stopped. It should also work on a separate PC with a USB-attached comma " + \
"panda. Vehicle ignition must be on. Recommend engine not be running when making changes. Must " + \
"turn ignition off and on again for any changes to take effect."
parser = argparse.ArgumentParser(description=desc_text, epilog=epilog_text)
parser.add_argument("--debug", action="store_true", help="enable ISO-TP/UDS stack debugging output")
parser.add_argument("action", choices={"show", "enable", "disable"}, help="show or modify current EPS HCA config")
args = parser.parse_args()
panda = Panda()
panda.set_safety_mode(Panda.SAFETY_ELM327)
bus = 1 if panda.has_obd() else 0
uds_client = UdsClient(panda, MQB_EPS_CAN_ADDR, MQB_EPS_CAN_ADDR + RX_OFFSET, bus, timeout=0.2, debug=args.debug)
try:
uds_client.diagnostic_session_control(SESSION_TYPE.EXTENDED_DIAGNOSTIC)
except MessageTimeoutError:
print("Timeout opening session with EPS")
quit()
odx_file, current_coding = None, None
try:
hw_pn = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_ECU_HARDWARE_NUMBER).decode("utf-8")
sw_pn = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_SPARE_PART_NUMBER).decode("utf-8")
sw_ver = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_ECU_SOFTWARE_VERSION_NUMBER).decode("utf-8")
component = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.SYSTEM_NAME_OR_ENGINE_TYPE).decode("utf-8")
odx_file = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.ODX_FILE).decode("utf-8").rstrip('\x00')
current_coding = uds_client.read_data_by_identifier(VOLKSWAGEN_DATA_IDENTIFIER_TYPE.CODING) # type: ignore
coding_text = current_coding.hex()
print("\nEPS diagnostic data\n")
print(f" Part No HW: {hw_pn}")
print(f" Part No SW: {sw_pn}")
print(f" SW Version: {sw_ver}")
print(f" Component: {component}")
print(f" Coding: {coding_text}")
print(f" ASAM Dataset: {odx_file}")
except NegativeResponseError:
print("Error fetching data from EPS")
quit()
except MessageTimeoutError:
print("Timeout fetching data from EPS")
quit()
coding_variant, current_coding_array, coding_byte, coding_bit = None, None, 0, 0
coding_length = len(current_coding)
# EPS_MQB_ZFLS
if odx_file in ("EV_SteerAssisMQB", "EV_SteerAssisMNB"):
coding_variant = "ZFLS"
coding_byte = 0
coding_bit = 4
# MQB_PP_APA, MQB_VWBS_GEN2
elif odx_file in ("EV_SteerAssisVWBSMQBA", "EV_SteerAssisVWBSMQBGen2"):
coding_variant = "APA"
coding_byte = 3
coding_bit = 0
else:
print("Configuration changes not yet supported on this EPS!")
quit()
current_coding_array = struct.unpack(f"!{coding_length}B", current_coding)
hca_enabled = (current_coding_array[coding_byte] & (1 << coding_bit) != 0)
hca_text = ("DISABLED", "ENABLED")[hca_enabled]
print(f" Lane Assist: {hca_text}")
try:
params = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION).decode("utf-8")
param_version_system_params = params[1:3]
param_vehicle_type = params[3:5]
param_index_char_curve = params[5:7]
param_version_char_values = params[7:9]
param_version_memory_map = params[9:11]
print("\nEPS parameterization (per-vehicle calibration) data\n")
print(f" Version of system parameters: {param_version_system_params}")
print(f" Vehicle type: {param_vehicle_type}")
print(f" Index of characteristic curve: {param_index_char_curve}")
print(f" Version of characteristic values: {param_version_char_values}")
print(f" Version of memory map: {param_version_memory_map}")
except (NegativeResponseError, MessageTimeoutError):
print("Error fetching parameterization data from EPS!")
quit()
if args.action in ["enable", "disable"]:
print("\nAttempting configuration update")
assert(coding_variant in ("ZFLS", "APA"))
# ZFLS EPS config coding length can be anywhere from 1 to 4 bytes, but the
# bit we care about is always in the same place in the first byte
if args.action == "enable":
new_byte = current_coding_array[coding_byte] | (1 << coding_bit)
else:
new_byte = current_coding_array[coding_byte] & ~(1 << coding_bit)
new_coding = current_coding[0:coding_byte] + new_byte.to_bytes(1, "little") + current_coding[coding_byte+1:]
try:
seed = uds_client.security_access(ACCESS_TYPE_LEVEL_1.REQUEST_SEED) # type: ignore
key = struct.unpack("!I", seed)[0] + 28183 # yeah, it's like that
uds_client.security_access(ACCESS_TYPE_LEVEL_1.SEND_KEY, struct.pack("!I", key)) # type: ignore
except (NegativeResponseError, MessageTimeoutError):
print("Security access failed!")
print("Open the hood and retry (disables the \"diagnostic firewall\" on newer vehicles)")
quit()
try:
# Programming date and tester number must be written before making
# a change, or write to CODING will fail with request sequence error
# Encoding on tester is unclear, it contains the workshop code in the
# last two bytes, but not the VZ/importer or tester serial number
# Can't seem to read it back, but we can read the calibration tester,
# so fib a little and say that same tester did the programming
current_date = date.today()
formatted_date = current_date.strftime('%y-%m-%d')
year, month, day = (int(part) for part in formatted_date.split('-'))
prog_date = bytes([year, month, day])
uds_client.write_data_by_identifier(DATA_IDENTIFIER_TYPE.PROGRAMMING_DATE, prog_date)
tester_num = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.CALIBRATION_REPAIR_SHOP_CODE_OR_CALIBRATION_EQUIPMENT_SERIAL_NUMBER)
uds_client.write_data_by_identifier(DATA_IDENTIFIER_TYPE.REPAIR_SHOP_CODE_OR_TESTER_SERIAL_NUMBER, tester_num)
uds_client.write_data_by_identifier(VOLKSWAGEN_DATA_IDENTIFIER_TYPE.CODING, new_coding) # type: ignore
except (NegativeResponseError, MessageTimeoutError):
print("Writing new configuration failed!")
print("Make sure the comma processes are stopped: tmux kill-session -t comma")
quit()
try:
# Read back result just to make 100% sure everything worked
current_coding_text = uds_client.read_data_by_identifier(VOLKSWAGEN_DATA_IDENTIFIER_TYPE.CODING).hex() # type: ignore
print(f" New coding: {current_coding_text}")
except (NegativeResponseError, MessageTimeoutError):
print("Reading back updated coding failed!")
quit()
print("EPS configuration successfully updated")