Otomotiv Middleware
TEKNİK REHBER OTOMOTİV MİDDLEWARE UDS ISO 14229 2026

UDS
ISO 14229 araç diagnostics protokolü.

Tüm OEM'lerin kullandığı standart — session kontrolü, güvenlik erişimi, DTC yönetimi ve yazılım güncellemesi tek protokolde.

00 UDS nedir — ISO 14229

UDS (Unified Diagnostic Services), ISO 14229-1 standardıyla tanımlanmış araç diagnostics protokolüdür. Tüm büyük OEM'ler (BMW, Mercedes, VW, Toyota, Ford) bu standardı uygular.

UDS'nin temel işlevleri şunlardır:

  • ECU'ya bağlan ve teşhis session'ı aç
  • Hata kodlarını (DTC) oku ve sil
  • ECU parametrelerini oku/yaz (VIN, kalibrasyon değerleri)
  • Güvenlik erişimi ile yazılım güncellemesi yap (flash)
  • Rutin testleri çalıştır (sensör kalibrasyonu, aktuatör testi)
  ┌──────────────────────────────────────────────────────────┐
  │                   UDS Kullanım Alanı                     │
  ├──────────────────────────────────────────────────────────┤
  │  Servis Merkezi      │ Araç bakımı, DTC okuma/silme      │
  │  OEM Test Hattı      │ Üretim sonu kalibrasyon, flash    │
  │  OTA Güncelleme      │ DoIP + UDS ile uzaktan flash      │
  │  Araç Geliştirme     │ ECU kalibrasyonu, değer yazma     │
  │  Araştırma           │ Araç güvenlik analizi             │
  └──────────────────────────────────────────────────────────┘
    

Temel Kavramlar

Tester
Teşhis cihazı. Fiziksel OBD-II dongle, laptop yazılımı veya Python scripti olabilir.
ECU (Server)
UDS servislerini sunan araç birimi. Her ECU farklı bir adrese sahiptir.
DID
Data Identifier — 2 byte'lık veri nesnesi kimliği. VIN = 0xF190, software version = 0xF101.
DTC
Diagnostic Trouble Code — arıza kodu. ISO 15031-6 formatı: P0301 (ateşleme hatası) gibi.
Service ID (SID)
1 byte'lık hizmet kodu. İstek için doğrudan SID, yanıt için SID + 0x40 kullanılır.
ISO 14229 YAPILANMASI

ISO 14229 çok bölümlüdür: -1 uygulama katmanı (bu rehber), -2 session ve transport, -3 UDS on CAN, -5 UDS on Internet. DoIP (ISO 13400) ile birlikte UDS, Ethernet üzerinden de çalışır.

01 OSI katman modeli

UDS, uygulama katmanı protokolüdür. Altındaki katmanlar farklı fiziksel ortamlara göre değişebilir — CAN veya Ethernet.

  ┌─────────────────────────────────────────────────────────────┐
  │  Uygulama Katmanı   │  UDS (ISO 14229-1)                    │
  │                     │  Service: 0x22 ReadDataByIdentifier   │
  ├─────────────────────┼─────────────────────────────────────┤
  │  Transport Katmanı  │  ISO-TP (ISO 15765-2)                 │
  │                     │  Single/First/Consecutive Frame       │
  ├──────────┬──────────┴─────────────────────────────────────┤
  │  CAN yolu│  Ağ Katmanı   │  IPv4 / IPv6                    │
  │          │  Veri Katmanı │  CAN 2.0B / CAN FD              │
  │          │               │                                  │
  │  IP yolu │  Transport    │  TCP (DoIP)                     │
  │          │  Ağ           │  IPv4 / IPv6                    │
  │          │  Veri         │  Ethernet                       │
  └──────────┴───────────────┴─────────────────────────────────┘
    

ISO-TP (ISO 15765-2)

CAN frame'i maksimum 8 byte taşır. UDS mesajları daha büyük olabileceğinden ISO-TP segmentasyon/yeniden birleştirme yapar:

Single Frame (SF)
Toplam veri 7 byte veya daha az. Tek CAN frame'e sığar. PCI byte: 0x0N (N = uzunluk).
First Frame (FF)
Çok parçalı mesajın ilk frame'i. PCI: 0x1NNN (NNN = toplam uzunluk). İlk 6 byte veri içerir.
Consecutive Frame (CF)
Devam frame'leri. PCI: 0x2N (N = sıra numarası 1–15, sonra 0'a döner).
Flow Control (FC)
Alıcı, göndereni yavaşlatmak için FC gönderir. BlockSize ve STmin parametrelerini belirler.
ISO-TP örnek — UDS Request (0x22 F190 = VIN oku)
# Tester → ECU (CAN ID: 0x7DF broadcast veya 0x7E0 + ECU offset)
# Single Frame: PCI=0x03 (3 byte), SID=0x22, DID=0xF190
CAN ID: 0x7DF
Data:   03 22 F1 90 00 00 00 00

# ECU → Tester (First Frame: toplam 20 byte VIN yanıtı)
CAN ID: 0x7E8
Data:   10 14 62 F1 90 31 47 31   # FF: len=0x14=20, SID+0x40=0x62

# Tester → ECU (Flow Control: devam et)
CAN ID: 0x7DF
Data:   30 00 00 00 00 00 00 00   # FC: BS=0 (sınırsız), STmin=0

# ECU → Tester (Consecutive Frame 1)
CAN ID: 0x7E8
Data:   21 46 4A 31 32 33 34 35   # CF sn=1

# ECU → Tester (Consecutive Frame 2)
CAN ID: 0x7E8
Data:   22 36 37 38 39 30 31 32   # CF sn=2, VIN tamamlandı

02 Service ID tablosu

UDS, her biri bir araç diagnostics işlevi için ayrılmış Service ID'lerden oluşur. Yanıtlar her zaman SID + 0x40 şeklindedir; hata yanıtı 0x7F SID NRC formatındadır.

0x10 — DiagnosticSessionControl
Teşhis session'ını değiştirir. Sub-func: 0x01=Default, 0x02=Programming, 0x03=Extended.
0x11 — ECUReset
ECU'yu yeniden başlatır. Sub-func: 0x01=HardReset, 0x02=KeyOffOnReset, 0x03=SoftReset.
0x14 — ClearDiagnosticInformation
DTC'leri siler. Parametre: DTC group (0xFFFFFF = tüm DTC'ler).
0x19 — ReadDTCInformation
DTC listesini ve detaylarını okur. Birçok sub-function ile farklı filtreleme imkânı sunar.
0x22 — ReadDataByIdentifier
DID (Data Identifier) ile veri okur. Tek istekte birden fazla DID sorgulanabilir.
0x27 — SecurityAccess
Güvenlik kilidi açar. Seed/Key mekanizması. Hassas işlemler (flash, kalibrasyon) için gereklidir.
0x2E — WriteDataByIdentifier
DID'e veri yazar. SecurityAccess sonrası kullanılır. VIN, kalibrasyon verisi yazılabilir.
0x31 — RoutineControl
Aktuatör testi, kalibrasyon rutini gibi ECU içi prosedürleri tetikler. Start/Stop/Result sub-func.
0x34 — RequestDownload
ECU'ya veri (firmware) indirme işlemini başlatır. Adres ve boyut bilgisi içerir.
0x36 — TransferData
0x34 sonrası veri bloklarını ECU'ya iletir. Block Sequence Counter ile sıra doğrulaması yapılır.
0x37 — RequestTransferExit
Transfer tamamlandığında oturumu kapatır. Checksum doğrulaması burada yapılabilir.
0x3E — TesterPresent
Non-default session'ı canlı tutar. ECU, belirli süre TesterPresent görmezse Default Session'a döner.

Negatif Yanıt Kodları (NRC)

Negatif yanıt formatı ve yaygın kodlar
# Format: 7F [SID] [NRC]
# Örnek: 0x27 SecurityAccess'e hatalı key gönderildi
7F 27 35   # NRC 0x35 = invalidKey

# Yaygın NRC kodları
# 0x10 — generalReject
# 0x11 — serviceNotSupported
# 0x12 — subFunctionNotSupported
# 0x13 — incorrectMessageLengthOrInvalidFormat
# 0x22 — conditionsNotCorrect (wrong session)
# 0x24 — requestSequenceError (wrong order)
# 0x31 — requestOutOfRange (invalid DID)
# 0x33 — securityAccessDenied
# 0x35 — invalidKey
# 0x36 — exceededNumberOfAttempts
# 0x37 — requiredTimeDelayNotExpired
# 0x78 — requestCorrectlyReceivedResponsePending (ECU meşgul, bekle)

03 Session yönetimi

UDS üç temel session türü tanımlar. Her session farklı bir yetki seviyesini ve erişilebilir servisleri kapsar.

Default Session (0x01)
ECU başlarken girer. Araç çalışırken daima bu session aktiftir. Temel okuma işlemleri izinlidir.
Programming Session (0x02)
Yazılım güncellemesi için. Firmware flash işlemleri (0x34/36/37) bu session'da yapılır. ECU genellikle boot mode'a geçer.
Extended Diagnostic Session (0x03)
Gelişmiş kalibrasyon ve güvenlik erişimi. SecurityAccess servisi bu session'da kullanılır.

Session Zamanlama Parametreleri

P2 (50ms tipik)
Tester'ın yanıt bekleme süresi. ECU bu süre içinde yanıt veya 0x78 (pending) göndermelidir.
P2* (5000ms tipik)
0x78 pending aldıktan sonra genişletilmiş bekleme süresi. Flash işlemleri uzun sürebilir.
S3 (5000ms tipik)
Non-default session'da TesterPresent mesajları arası maksimum süre. Aşılırsa Default Session'a dönülür.
Session geçiş akışı
# 1. Default → Extended Session aç
# İstek:  10 03
# Yanıt:  50 03 00 32 01 F4  (P2=50ms, P2*=500ms)

# 2. Session canlı tut (her 4.5 saniyede bir)
# İstek:  3E 00  (TesterPresent, suppress response)
# Yanıt:  7E 00  (ya da suppress bit ile yanıt yok)

# 3. Default Session'a dön
# İstek:  10 01
# Yanıt:  50 01 ...

# 4. ECU Reset
# İstek:  11 01  (Hard Reset)
# Yanıt:  51 01  (sonra ECU yeniden başlar)
S3 TIMER

Extended veya Programming session'da çalışırken S3 timer'ı aşmamaya dikkat et. Script içinde TesterPresent (0x3E 0x80) mesajlarını periyodik gönder. 0x80 sub-function "suppressPosRspMsgIndicationBit" anlamına gelir — yanıt istemediğini belirtir ve ağ trafiğini azaltır.

04 SecurityAccess — seed/key mekanizması

Hassas ECU işlemleri (yazılım güncelleme, kalibrasyon yazma) güvenlik kilidi arkasında bulunur. SecurityAccess (0x27) servisi challenge-response yöntemiyle bu kilidi açar.

Seed/Key Akışı

  Tester                                    ECU
    │                                         │
    │── 27 01 (requestSeed, level 1) ────────►│
    │                                         │  random seed üret
    │◄─ 67 01 [seed bytes] ───────────────────│
    │                                         │
    │  key = f(seed, secret_algorithm)        │
    │                                         │
    │── 27 02 [key bytes] ────────────────────►│
    │                              key doğrula│
    │◄─ 67 02 (accessGranted) ────────────────│
    │                                         │
    │  Artık kısıtlı servisler kullanılabilir │
    

Security Level'lar

Her OEM farklı security level'lar tanımlar. Genel kural:

  • Odd sub-function (0x01, 0x03, 0x05...): seed iste
  • Even sub-function (0x02, 0x04, 0x06...): key gönder
  • Level 0x01/0x02: geliştirici erişimi
  • Level 0x11/0x12: ECU supplier erişimi
  • Level 0x21/0x22: OEM erişimi (üretim hattı)

Brute Force Koruması

ECU, ardışık yanlış key denemelerini sayar. Genellikle 3 yanlış denemeden sonra:

  • Belirli süre (delay timer) yeni seed talebi reddeder (NRC 0x37)
  • Bazı ECU'larda kalıcı kilitleme gerektirir servis merkezi müdahalesi
Örnek seed/key algoritması (basit XOR — gerçek değil)
def calculate_key(seed: bytes, secret: int = 0xABCD1234) -> bytes:
    """
    Gerçek araçlarda bu algoritma OEM tarafından gizli tutulur.
    Tersine mühendislik veya NDA ile temin edilir.
    """
    seed_int = int.from_bytes(seed, 'big')
    key_int = seed_int ^ secret
    key_int = ((key_int << 3) | (key_int >> 29)) & 0xFFFFFFFF
    return key_int.to_bytes(4, 'big')

# Kullanım:
seed = bytes.fromhex('12 34 56 78'.replace(' ', ''))
key  = calculate_key(seed)
print(f"Seed: {seed.hex()}, Key: {key.hex()}")
GÜVENLİK NOTU

Gerçek araçlarda SecurityAccess algoritması OEM sırrıdır. GDPR ve araç güvenliği yasaları kapsamında bu algoritmanın izinsiz tersine mühendisliği yasal sorun yaratabilir. Araştırma ortamında kendi test ECU'nuzla çalışın.

05 DID — Data Identifier okuma/yazma

DID (Data Identifier), ECU içindeki veri nesnelerini tanımlayan 2 byte'lık anahtarlardır. ISO 14229 bazı standart DID'leri tanımlar; OEM'ler kendi özel DID'lerini ekler.

Standart DID'ler

0xF190 — VINDataIdentifier
17 karakter araç kimlik numarası (VIN). ISO 3779 formatı. Örnek: 1G1JC5SH2F4176831.
0xF101 — ECUSoftwareVersionNumber
ECU yazılım versiyon numarası. Format OEM'e göre değişir.
0xF181 — ApplicationSoftwareFingerprint
Yüklü yazılımın parmak izi (hash veya versiyon bilgisi).
0xF186 — ActiveDiagnosticSessionDataIdentifier
Mevcut aktif session ID'sini döner.
0xF18A — SystemSupplierECUSoftwareVersionNumber
ECU tedarikçi yazılım versiyonu.
0xF18B — ECUManufacturingDate
ECU üretim tarihi (YYYYMMDD ASCII formatında).
0xF18C — ECUSerialNumber
ECU seri numarası.

Tek istek — birden fazla DID

UDS ReadDataByIdentifier — ham çerçeve
# Tek DID: VIN oku
# İstek:  22 F1 90
# Yanıt:  62 F1 90 [17 byte VIN ASCII]

# İki DID aynı anda: VIN + SW Version
# İstek:  22 F1 90 F1 01
# Yanıt:  62 F1 90 [17 byte] F1 01 [N byte versiyon]

# Örnek yanıt (ASCII VIN: "1G1JC5444Y7123456")
62 F1 90 \
31 47 31 4A 43 35 34 34 \   # 1G1JC544
34 59 37 31 32 33 34 35 \   # 4Y712345
36                          # 6

DID Yazma — WriteDataByIdentifier (0x2E)

UDS WriteDataByIdentifier — VIN yaz (kalibrasyon hattında)
# Önce Extended Session ve SecurityAccess gerekli
# İstek:  2E F1 90 [17 byte VIN]
# Yanıt:  6E F1 90  (başarılı)
# Hata:   7F 2E 22  (conditionsNotCorrect — wrong session)

# Yeni VIN: "WBAC5441X1LB12345"
2E F1 90 \
57 42 41 43 35 34 34 31 \   # WBAC5441
58 31 4C 42 31 32 33 34 \   # X1LB1234
35                          # 5

06 DTC okuma ve yönetimi

0x19 ReadDTCInformation servisi, ECU'da kayıtlı arıza kodlarını (DTC) çeşitli kriterlere göre sorgular. DTC statusByte, arızanın mevcut durumunu kodlar.

0x19 Sub-function'ları

0x01 — reportNumberOfDTCByStatusMask
Belirtilen status maskesine uyan DTC sayısını döner. Hızlı sayım için kullanılır.
0x02 — reportDTCByStatusMask
Status maskesine uyan tüm DTC'leri listeler. En yaygın kullanılan sub-function.
0x06 — reportDTCExtDataRecordByDTCNumber
Belirli bir DTC için genişletilmiş veri (freeze frame, oluşma sayısı) döner.
0x0A — reportSupportedDTC
ECU'nun desteklediği tüm DTC'leri listeler. Araç geliştirme ve validasyon için kullanılır.

statusByte Anatomisi

  Bit 7  Bit 6  Bit 5  Bit 4  Bit 3  Bit 2  Bit 1  Bit 0
  ──────────────────────────────────────────────────────
   WIR   TNASF   TNCSLC  PDTC  CDTC  PDTCSNC  TFTOC  TF
    │      │       │       │     │      │         │     │
    │      │       │       │     │      │         │     └─ testFailed (şu an arıza var)
    │      │       │       │     │      │         └─ testFailedThisOperationCycle
    │      │       │       │     │      └─ pendingDTC (bu sürüş döngüsünde başarısız)
    │      │       │       │     └─ confirmedDTC (yeterince tekrar etti)
    │      │       │       └─ pendingDTCnotConfirmed
    │      │       └─ testNotCompletedSinceLastClear
    │      └─ testNotCompletedThisOperationCycle
    └─ warningIndicatorRequested (MIL lambası)
    
DTC okuma — ham çerçeve
# Tüm aktif (testFailed) DTC'leri iste: statusMask = 0x01
# İstek:  19 02 01
# Yanıt:  59 02 FF [DTC1 3 byte] [status1] [DTC2 3 byte] [status2] ...

# Yanıt parse:
# 59 02 FF        — SID+0x40, sub-func, DTC format
# 00 12 34 09     — DTC: P0x1234, status: 0x09 (TF=1, confirmed=1)
# 00 45 67 0F     — DTC: P0x4567, status: 0x0F

# Tüm DTC'leri sil
# İstek:  14 FF FF FF
# Yanıt:  54

DTC Formatı

ISO 15031-6 DTC formatı: 3 byte. İlk 2 bit domain kodunu, geri kalanı arıza numarasını belirler.

Python — DTC decode
def decode_dtc(dtc_bytes: bytes) -> str:
    domain = {0: 'P', 1: 'C', 2: 'B', 3: 'U'}
    high = dtc_bytes[0]
    code = ((high & 0x3F) << 8) | dtc_bytes[1]
    d = domain[(high >> 6) & 0x03]
    return f"{d}{code:04X}"

# Örnek: 0x001234 → P1234
print(decode_dtc(bytes([0x00, 0x12, 0x34])))  # P1234
print(decode_dtc(bytes([0x40, 0x45, 0x67])))  # C4567

07 python-udsoncan ile UDS istemcisi

python-udsoncan, UDS protokolünü Python'da kolayca kullanmanızı sağlayan bir kütüphanedir. ISO-TP transport katmanı için python-can veya doipclient ile çalışır.

Kurulum

bash
pip install udsoncan python-can
# DoIP transport için:
pip install doipclient

CAN üzerinde UDS — tam örnek

Python
import udsoncan
from udsoncan.connections import PythonIsoTpConnection
from udsoncan.client import Client
import isotp
import can

# CAN bus ve ISO-TP bağlantısı kur (SocketCAN: vcan0)
bus = can.interface.Bus(channel='vcan0', bustype='socketcan')
tp_addr = isotp.Address(isotp.AddressingMode.Normal_11bits,
                        txid=0x7E0, rxid=0x7E8)
stack = isotp.CanStack(bus=bus, address=tp_addr)
conn  = PythonIsoTpConnection(stack)

config = udsoncan.configs.default_client_config.copy()
config['request_timeout'] = 5

with Client(conn, config=config) as client:

    # 1. Extended Session aç
    client.change_session(
        udsoncan.services.DiagnosticSessionControl.Session.extendedDiagnosticSession)
    print("Extended session açıldı")

    # 2. SecurityAccess — seed iste, key hesapla, gönder
    result = client.request_seed(0x01)
    seed = result.service_data.seed
    key  = calculate_key(seed)   # OEM'e özgü algoritma
    client.send_key(0x02, key)
    print("SecurityAccess başarılı")

    # 3. VIN oku
    result = client.read_data_by_identifier([0xF190])
    vin = result.service_data.values[0xF190].decode('ascii')
    print(f"VIN: {vin}")

    # 4. DTC listesini oku
    result = client.get_dtc_by_status_mask(0xFF)
    for dtc_entry in result.service_data.dtcs:
        print(f"DTC: {dtc_entry.id:06X}  Status: {dtc_entry.status.get_byte():02X}")

    # 5. DTC'leri temizle
    client.clear_dtc(0xFFFFFF)
    print("DTC'ler temizlendi")

    # 6. ECU soft reset
    client.ecu_reset(udsoncan.services.ECUReset.ResetType.softReset)

TesterPresent döngüsü

Python — background TesterPresent thread
import threading, time

def keep_session_alive(client, interval=4.5):
    while not stop_event.is_set():
        try:
            client.tester_present(suppress_positive_response=True)
        except:
            pass
        time.sleep(interval)

stop_event = threading.Event()
tp_thread = threading.Thread(target=keep_session_alive,
                             args=(client,), daemon=True)
tp_thread.start()
# ... UDS işlemler ...
stop_event.set()

08 Pratik: ECU tester ve yazılım indirme

Raspberry Pi + MCP2515 SPI-CAN modülü ile gerçek CAN bus'a bağlan, VIN oku ve UDS 0x34/36/37 servisleriyle yazılım indirme akışını uygula.

Donanım Kurulumu

bash — Raspberry Pi MCP2515 kurulumu
# /boot/config.txt eklemeleri
dtparam=spi=on
dtoverlay=mcp2515-can0,oscillator=8000000,interrupt=25

# CAN interface'i başlat
sudo ip link set can0 up type can bitrate 500000
ip -details link show can0

# Test: candump ile CAN trafiği izle
candump can0

Yazılım İndirme Akışı (0x34/36/37)

  Tester                                    ECU
    │                                         │
    │── 10 02 (Programming Session) ─────────►│
    │◄─ 50 02 ────────────────────────────────│
    │                                         │
    │── 27 11 (Seed Request) ─────────────────►│
    │◄─ 67 11 [seed] ─────────────────────────│
    │── 27 12 [key]  ─────────────────────────►│
    │◄─ 67 12 ────────────────────────────────│
    │                                         │
    │── 34 00 44 [addr:4] [len:4] ────────────►│  RequestDownload
    │◄─ 74 20 [maxBlockLen:2] ────────────────│  maxBlockSize
    │                                         │
    │── 36 01 [block data] ──────────────────►│  TransferData blk 1
    │◄─ 76 01 ────────────────────────────────│
    │── 36 02 [block data] ──────────────────►│  TransferData blk 2
    │◄─ 76 02 ────────────────────────────────│
    │  ... (tüm bloklar) ...                  │
    │── 37 (RequestTransferExit) ────────────►│
    │◄─ 77 ───────────────────────────────────│
    │── 31 01 FF 01 (CheckProgrammingDep.) ──►│  Opsiyonel
    │◄─ 71 01 FF 01 ──────────────────────────│
    │── 11 01 (ECU Hard Reset) ──────────────►│
    
Python — firmware flash akışı
import udsoncan
from udsoncan.client import Client

def flash_firmware(client, firmware_path: str, flash_addr: int):
    with open(firmware_path, 'rb') as f:
        firmware = f.read()

    # Programming session
    client.change_session(
        udsoncan.services.DiagnosticSessionControl.Session.programmingSession)

    # Security access
    seed = client.request_seed(0x11).service_data.seed
    client.send_key(0x12, calculate_key(seed))

    # RequestDownload: mem addr 4 byte, len 4 byte
    mem_addr = udsoncan.MemoryLocation(
        address=flash_addr,
        memorysize=len(firmware),
        address_format=32, memorysize_format=32)

    result = client.request_download(
        memory_location=mem_addr,
        dfi=udsoncan.DataFormatIdentifier(0x00))  # no compression

    max_block = result.service_data.max_length
    print(f"Max blok boyutu: {max_block} byte")

    # TransferData bloklar halinde
    block_seq = 1
    offset = 0
    while offset < len(firmware):
        chunk = firmware[offset:offset + max_block - 2]
        client.transfer_data(block_seq, chunk)
        offset += len(chunk)
        block_seq = (block_seq % 0xFF) + 1
        print(f"  {offset}/{len(firmware)} byte aktarıldı")

    # Transfer tamamla
    client.request_transfer_exit()
    print("Flash tamamlandı, ECU resetleniyor...")

    # ECU reset
    client.ecu_reset(udsoncan.services.ECUReset.ResetType.hardReset)

# Örnek kullanım:
# flash_firmware(client, 'firmware_v2.bin', 0x08000000)
CANdela Studio

CANdela Studio (Vector Informatik), araç OEM'lerinin UDS servis tanımlarını (.cdd dosyası) oluşturmak için kullandığı ticari araçtır. python-udsoncan, .cdd dosyalarından otomatik DID/DTC yapılarını yükleyebilir. Açık kaynak alternatif: diag-gui veya python-uds.