VW MQB: EPS configuration tool (#23737)
* VW MQB: EPS configuration tool * a bit more help text * typo * Update selfdrive/debug/vw_mqb_config.py Co-authored-by: Willem Melching <willem.melching@gmail.com> * Update selfdrive/debug/vw_mqb_config.py Co-authored-by: Willem Melching <willem.melching@gmail.com> * better handling of vendor constants * comment tweak * more constant clarity * move help text to argparse * add parameterization parsing * refactor param read, skip length byte * touch up section headers * removing; low value and acting weird Co-authored-by: Willem Melching <willem.melching@gmail.com>pull/23742/head
parent
cdf0bb887a
commit
4416c21b1e
2 changed files with 168 additions and 0 deletions
@ -0,0 +1,145 @@ |
||||
#!/usr/bin/env python3 |
||||
|
||||
import argparse |
||||
import struct |
||||
from enum import IntEnum |
||||
from panda import Panda |
||||
from panda.python.uds import UdsClient, MessageTimeoutError, NegativeResponseError, SESSION_TYPE,\ |
||||
DATA_IDENTIFIER_TYPE, ACCESS_TYPE |
||||
|
||||
# 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 two or 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") |
||||
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 = None, None |
||||
# EV_SteerAssisMQB covers the majority of MQB racks (EPS_MQB_ZFLS) |
||||
# APA racks (MQB_PP_APA) have a different coding layout, which should |
||||
# be easy to support once we identify the specific config bit |
||||
if odx_file == "EV_SteerAssisMQB\x00": |
||||
coding_variant = "ZF" |
||||
current_coding_array = struct.unpack("!4B", current_coding) |
||||
hca_enabled = (current_coding_array[0] & (1 << 4) != 0) |
||||
hca_text = ("DISABLED", "ENABLED")[hca_enabled] |
||||
print(f" Lane Assist: {hca_text}") |
||||
else: |
||||
print("Configuration changes not yet supported on this EPS!") |
||||
quit() |
||||
|
||||
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 == "ZF") # revisit when we have the APA rack coding bit |
||||
# ZF 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_0 = current_coding_array[0] | (1 << 4) |
||||
else: |
||||
new_byte_0 = current_coding_array[0] & ~(1 << 4) |
||||
new_coding = new_byte_0.to_bytes(1, "little") + current_coding[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!") |
||||
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 |
||||
# TODO: encode the actual current date |
||||
prog_date = b'\x22\x02\x08' |
||||
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!") |
||||
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") |
Loading…
Reference in new issue