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>
old-commit-hash: 4416c21b1e
			
			
				commatwo_master
			
			
		
							parent
							
								
									070f4ddcdc
								
							
						
					
					
						commit
						844aa7395a
					
				
				 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