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
Jason Young 2022-02-10 16:57:12 -05:00 committed by GitHub
parent cdf0bb887a
commit 4416c21b1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 168 additions and 0 deletions

View File

@ -34,3 +34,26 @@ optional arguments:
--addr ADDR
--values VALUES values to monitor (instead of entire event)
```
## [vw_mqb_config.py](vw_mqb_config.py)
```
usage: vw_mqb_config.py [-h] [--debug] {enable,show,disable}
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).
positional arguments:
{enable,show,disable}
show or modify current EPS HCA config
optional arguments:
-h, --help show this help message and exit
--debug enable ISO-TP/UDS stack debugging output
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.
```

View File

@ -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")