Tüm eğitimler
TEKNİK REHBERGÖMÜLÜ LİNUXARAÇ OTOMASYON2026

ISO-TP / UDS
CAN Tanılama Protokolleri

Linux SocketCAN üzerinde ISO 15765-2 taşıma katmanı (ISO-TP) ve ISO 14229 UDS (Unified Diagnostic Services) protokolleri ile araç ECU tanılama uygulamaları.

00 CAN Tanılama Temelleri

Modern araçlarda yüzlerce elektronik kontrol ünitesi (ECU) CAN ağı üzerinden haberleşir. Bu ECU'ların tanılanması, kalibre edilmesi ve güncellenmesi için standartlaştırılmış protokollere ihtiyaç vardır. OBD-II, ISO-TP ve UDS bu standartlar zincirinin temel halkalarıdır.

8 Bayt Sınırı ve ISO-TP Neden Gerekli?

Klasik CAN çerçevesi en fazla 8 bayt veri taşıyabilir. Firmware güncellemesi, arıza kodu listesi veya kalibrasyon verisi gibi büyük veri blokları bu sınır dahilinde iletilemez. ISO 15765-2 (ISO-TP) bu sorunu çözer: CAN çerçevelerini birleştirerek 4095 bayta kadar veri transferini mümkün kılar.

  +---------+          +-----------+          +----------+
  |  OBD-II |          |  UDS      |          | Özel     |
  |  SAE    |          |  ISO 14229|          | Protokol |
  | J1979   |          |           |          |          |
  +---------+          +-----------+          +----------+
       |                    |                      |
       +--------------------+----------------------+
                            | Uygulama Katmanı
                   +--------+--------+
                   |  ISO-TP         |
                   |  ISO 15765-2    |  Taşıma Katmanı
                   +--------+--------+
                            |
              +-------------+-------------+
              |  CAN 2.0A / 2.0B         |
              |  11-bit / 29-bit ID      |  Veri Bağı
              +---------------------------+
  

Protokol Ailesi ve Standartlar

StandartAdAçıklama
ISO 15765-2ISO-TPCAN üzerinde çok çerçeveli mesaj taşıma (maks. 4095 bayt)
ISO 14229UDSUnified Diagnostic Services — ECU tanılama ve programlama
SAE J1979OBD-IITeşhis PID'leri — motor, emisyon, sensör verileri
ISO 13400DoIPDiagnostics over IP — Ethernet tabanlı UDS taşıma
ISO 14230KWP2000Eski araçlarda K-Line ve CAN üzerinde tanılama
ISO 11898CANCAN fiziksel ve veri bağı katmanı

Linux SocketCAN Ekosistemi

Linux, CAN arayüzlerini standart ağ arayüzleri (örn. can0) olarak yönetir. AF_CAN soket ailesi üzerinden ham CAN, ISO-TP ve J1939 paketleri alınıp gönderilebilir.

# SocketCAN arayüzünü 500 kbit/s ile başlat
ip link set can0 type can bitrate 500000
ip link set can0 up

# Durum kontrol
ip -details link show can0

# Tüm CAN trafiğini izle
candump can0

# Basit OBD-II sorgusu: motor soğutma suyu sıcaklığı
# ID=0x7DF (broadcast), 3 bayt: Mode 01, PID 0x05
cansend can0 7DF#0301050000000000

OBD-II Konnektör Pinout (DLC)

PinSinyalAçıklama
4GND (Chassis)Şasi toprağı
5GND (Signal)Sinyal toprağı
6CAN-HYüksek hız CAN yüksek hattı (500 kbit/s)
14CAN-LYüksek hız CAN düşük hattı
16VBATAraç aküsü (12 V) — tanılama araçlarını besler

01 ISO-TP Protokolü

ISO 15765-2 (ISO-TP), 8 baytlık CAN çerçevelerini birleştirerek 4095 bayta kadar veri iletimi sağlar. Dört çerçeve tipi tanımlar: Single Frame, First Frame, Consecutive Frame ve Flow Control Frame.

Single Frame (SF) — Tek Çerçeve Mesaj

Mesaj 7 bayt veya daha kısaysa tek bir CAN çerçevesine sığar:

BaytAlanDeğerAçıklama
0 [7:4]PCI Type0x0Single Frame tipi göstergesi
0 [3:0]SF_DL1–7Mesaj verisi uzunluğu (bayt)
1–7DataUygulama verisi

First Frame (FF) ve Consecutive Frame (CF)

Mesaj 7 bayttan büyükse önce First Frame gönderilir, ardından Flow Control beklenir, sonra Consecutive Frame'ler sırayla iletilir:

ÇerçeveByte 0 [7:4]Temel AlanlarAçıklama
Single Frame (SF)0x0SF_DL[3:0], Data[1-7]Kısa mesaj — tek çerçeve
First Frame (FF)0x1FF_DL[11:0], Data[2-7]Uzun mesajın başlangıcı — toplam uzunluğu bildirir
Consecutive Frame (CF)0x2SN[3:0], Data[1-7]Ardışık veri parçaları — SN 0x1–0xF döngüsel
Flow Control (FC)0x3FS[1:0], BS, STminAlıcı hız kontrolü — Block Size ve minimum aralık

Çok Çerçeve İletişim Akışı

  Talep Eden (Tester)               Yanıt Veren (ECU)
       |                                    |
       |--- First Frame (FF) ----------->  |  Toplam: 100 bayt
       |                                    |
       |<-- Flow Control (FC) -----------   |  BS=5, STmin=10 ms
       |                                    |
       |--- Consecutive Frame SN=1 ------>  |
       |--- Consecutive Frame SN=2 ------>  |
       |--- Consecutive Frame SN=3 ------>  |
       |--- Consecutive Frame SN=4 ------>  |
       |--- Consecutive Frame SN=5 ------>  |  Block doldu
       |                                    |
       |<-- Flow Control (FC) -----------   |  BS=0 (tümünü gönder)
       |                                    |
       |--- Consecutive Frame SN=6 ------>  |
       |--- ... (kalan CF'ler) ---------->  |
  

Flow Control Frame Alanları

FS (Flow Status)0x0 = ContinueToSend, 0x1 = Wait (bekle), 0x2 = Overflow (tampon taştı — transfer iptal)
BS (Block Size)Bir sonraki FC'ye kadar gönderilebilecek CF sayısı. 0 = sınırsız (tümünü gönder).
STminArdışık CF'ler arası minimum süre. 0x00–0x7F = 0–127 ms; 0xF1–0xF9 = 100–900 µs.

Segment Birleştirme Mekanizması

ISO-TP alıcı taraf şu adımları izler: FF alınır ve toplam mesaj uzunluğu kayıt edilir, FC gönderilir, CF'ler SN sırasına göre birleştirilir, tüm baytlar alındığında tam PDU (Protocol Data Unit) üst katmana teslim edilir. Linux kernel'in can_isotp modülü bu birleştirme işlemini otomatik olarak gerçekleştirir.

02 Linux ISO-TP Soketi

Linux 5.10 ve sonrasında çekirdek içi ISO-TP soket desteği mevcuttur. Kullanıcı alanı uygulamaları AF_CAN / SOCK_DGRAM / CAN_ISOTP üçlüsüyle kernel'in çerçeveleme katmanını kullanabilir.

Kernel Modülü ve Bağımlılıklar

# ISO-TP modülünü yükle
modprobe can
modprobe can_raw
modprobe can_isotp

# Modül yüklü mü?
lsmod | grep isotp
# can_isotp    32768  0

# Kernel config kontrolü
zcat /proc/config.gz | grep CONFIG_CAN_ISOTP
# CONFIG_CAN_ISOTP=m

# can0 arayüzünü başlat
ip link set can0 type can bitrate 500000
ip link set can0 up

sockaddr_can Yapısı — ISO-TP Adresleme

/* linux/can/isotp.h — temel adres yapısı */
struct sockaddr_can {
    __kernel_sa_family_t  can_family;   /* AF_CAN */
    int                   can_ifindex;  /* arayuz indeksi (can0) */
    union {
        struct { canid_t rx_id, tx_id; } tp;  /* ISO-TP ID'leri */
        /* ... diger protokoller */
    } can_addr;
};

/* Tipik OBD-II ID atamalari */
/* tx_id = 0x7DF  (OBD-II broadcast tester ID)  */
/* rx_id = 0x7E8  (ECU 1 yanit ID)              */
/* rx_id = 0x7E9  (ECU 2 yanit ID)  vb.         */

can_isotp_fc_opts — Flow Control Parametreleri

bs (Block Size)Gönderilecek FC'deki BS değeri. 0 = tümünü gönder (test için önerilir).
stminGönderilecek FC'deki STmin değeri. 0x00 = bekleme yok.
wftmaxMaksimum Wait Frame sayısı. 0 = Wait Frame göndermez.

ISO-TP Soket Seçenekleri

Soket SeçeneğiYapıAçıklama
CAN_ISOTP_OPTScan_isotp_optionsBayraklar: padding, extend addr, half-duplex
CAN_ISOTP_RECV_FCcan_isotp_fc_optionsFC gönderme parametreleri: BS, STmin, WFTmax
CAN_ISOTP_TX_STMINuint32_tTX tarafı minimum çerçeve aralığı (µs)
CAN_ISOTP_RX_STMINuint32_tRX tarafı minimum CF aralığı — bu süreden önce gelen CF atılır
CAN_ISOTP_LL_OPTScan_isotp_ll_optionsCAN FD / BRS için link layer seçenekleri

Önemli can_isotp_options Bayrakları

BayrakDeğerAçıklama
CAN_ISOTP_LISTEN_MODE0x001Pasif dinleme — FC göndermez, izleme için
CAN_ISOTP_EXTEND_ADDR0x002Genişletilmiş adres (Byte 0 adres alanı)
CAN_ISOTP_TX_PADDING0x004TX çerçevelerini 8 bayta doldur
CAN_ISOTP_RX_PADDING0x008RX padding doğrulamasını etkinleştir
CAN_ISOTP_HALF_DUPLEX0x040Yarı çift yön modu (aynı ID TX/RX)

03 Temel ISO-TP Uygulaması (C)

Linux kernel ISO-TP soket API'si üzerinden C diliyle tam bir ISO-TP istemci örneği. Soket oluşturma, arayüz bağlama, veri gönderme ve almayi kapsar.

Temel ISO-TP Soket Örneği

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/isotp.h>
#include <net/if.h>
#include <sys/ioctl.h>

#define CAN_IFACE  "can0"
#define TESTER_ID  0x7DF   /* OBD-II broadcast tester ID */
#define ECU_ID     0x7E8   /* ECU yakit ID */

int isotp_open(const char *iface, canid_t tx_id, canid_t rx_id)
{
    int sock;
    struct sockaddr_can addr;
    struct ifreq ifr;

    /* 1) SOCK_DGRAM + CAN_ISOTP soketi olustur */
    sock = socket(AF_CAN, SOCK_DGRAM, CAN_ISOTP);
    if (sock < 0) {
        perror("socket");
        return -1;
    }

    /* 2) Arayuz indeksini al */
    strncpy(ifr.ifr_name, iface, IFNAMSIZ - 1);
    if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) {
        perror("ioctl SIOCGIFINDEX");
        close(sock);
        return -1;
    }

    /* 3) Adres yapisini doldur */
    memset(&addr, 0, sizeof(addr));
    addr.can_family        = AF_CAN;
    addr.can_ifindex       = ifr.ifr_ifindex;
    addr.can_addr.tp.tx_id = tx_id;
    addr.can_addr.tp.rx_id = rx_id;

    /* 4) Soketi bagla */
    if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("bind");
        close(sock);
        return -1;
    }

    return sock;
}

int main(void)
{
    int sock;
    unsigned char buf[4096];
    ssize_t nbytes;

    /* Soket ac */
    sock = isotp_open(CAN_IFACE, TESTER_ID, ECU_ID);
    if (sock < 0)
        return 1;

    /* FC parametrelerini ayarla */
    struct can_isotp_fc_options fc = {0};
    fc.bs    = 0;    /* sinırsız blok */
    fc.stmin = 0;    /* bekleme yok  */
    setsockopt(sock, SOL_CAN_ISOTP, CAN_ISOTP_RECV_FC, &fc, sizeof(fc));

    /* TX padding etkinlestir (bazi ECU'lar zorunlu kilar) */
    struct can_isotp_options opts = {0};
    opts.flags       = CAN_ISOTP_TX_PADDING;
    opts.txpad_content = 0xCC;
    setsockopt(sock, SOL_CAN_ISOTP, CAN_ISOTP_OPTS, &opts, sizeof(opts));

    /* UDS ReadDataByIdentifier 0x22 0xF1 0x90 (VIN) gonder */
    unsigned char req[] = { 0x22, 0xF1, 0x90 };
    if (write(sock, req, sizeof(req)) < 0) {
        perror("write");
        close(sock);
        return 1;
    }
    printf("Istek gonderildi: 22 F1 90\n");

    /* Yaniti oku — ISO-TP otomatik birlestirme yapar */
    nbytes = read(sock, buf, sizeof(buf));
    if (nbytes < 0) {
        perror("read");
    } else {
        printf("Yanit (%zd bayt):", nbytes);
        for (ssize_t i = 0; i < nbytes; i++)
            printf(" %02X", buf[i]);
        printf("\n");
        /* Pozitif yanitsa: 62 F1 90 [VIN 17 karakter] */
    }

    close(sock);
    return 0;
}

Derleme ve Çalıştırma

# Derleme (cross-compile ARM64 icin)
aarch64-linux-gnu-gcc -o isotp_client isotp_client.c

# can0 hazirla
sudo modprobe can_isotp
sudo ip link set can0 type can bitrate 500000
sudo ip link set can0 up

# Calistir
./isotp_client
# Istek gonderildi: 22 F1 90
# Yanit (20 bayt): 62 F1 90 57 41 55 5A 5A 5A ...  (VIN)

Zaman Aşımı ile Güvenli Okuma

#include <sys/select.h>

/* 2 saniye zaman asimi ile yanit bekleme */
int isotp_recv_timeout(int sock, unsigned char *buf, size_t len, int timeout_sec)
{
    fd_set rfds;
    struct timeval tv;

    FD_ZERO(&rfds);
    FD_SET(sock, &rfds);
    tv.tv_sec  = timeout_sec;
    tv.tv_usec = 0;

    int ret = select(sock + 1, &rfds, NULL, NULL, &tv);
    if (ret == 0) {
        fprintf(stderr, "Zaman asimi: ECU'dan yanit gelmedi\n");
        return -1;
    }
    if (ret < 0) {
        perror("select");
        return -1;
    }

    return (int)read(sock, buf, len);
}

isotpsend / isotprecv Araçları

# can-utils paketi gerekli
apt-get install can-utils

# Tek payload gonder (ISO-TP cercevelemesi otomatik)
isotpsend -s 7DF -d 7E8 can0 <<< "22 F1 90"

# Yanit bekle (arka planda)
isotprecv -s 7E8 -d 7DF can0

# ISO-TP trafik izleme
isotpdump -s 7DF -d 7E8 can0

04 UDS Servis Katmanı

UDS (ISO 14229 — Unified Diagnostic Services), ECU tanılama, kalibrasyon, veri okuma/yazma ve firmware güncelleme işlemlerini kapsamlı bir servis seti olarak tanımlar. Her servis bir Servis Tanımlayıcısı (SID) ile adreslenır.

Temel UDS Servisleri

SIDServis AdıAçıklama
0x10DiagnosticSessionControlOturum modu geçişi: Default, Extended, Programming
0x11ECUResetECU yeniden başlatma: Hard, Soft, KeyOffOn
0x14ClearDiagnosticInformationDTC kayıtlarını sil
0x19ReadDTCInformationDTC listesi ve durum bilgisi oku
0x22ReadDataByIdentifierDID ile veri oku — VIN, sensör değerleri, kalibrasyon
0x23ReadMemoryByAddressBelirli bellek adresinden ham veri oku
0x27SecurityAccessGüvenlik kilidi açma — seed/key mekanizması
0x28CommunicationControlNormal/NM mesaj gönderimini etkinleştir/devre dışı bırak
0x2EWriteDataByIdentifierDID ile veri yaz — kalibrasyon parametreleri
0x2FInputOutputControlByIdentifierI/O aktivasyonu: sensör simülasyonu, aktüatör kontrolü
0x31RoutineControlRutin başlat/durdur/sonuç al — test ve doğrulama rutinleri
0x34RequestDownloadFirmware/veri yükleme başlatma
0x36TransferDataVeri bloğu transferi (indirme veya yükleme)
0x37RequestTransferExitTransfer tamamlama ve bütünlük doğrulama
0x3ETesterPresentOturumu canlı tut — zaman aşımını engelle
0x85ControlDTCSettingDTC kayıt mekanizmasını etkinleştir/devre dışı bırak

UDS Oturum Modları

Sub-fnOturum ModuErişilebilir Başlıca Servisler
0x01DefaultSession0x10, 0x11, 0x19, 0x22, 0x3E — temel okuma
0x02ProgrammingSession0x10, 0x27, 0x34, 0x36, 0x37 — firmware güncelleme
0x03ExtendedDiagnosticSessionTüm servisler + 0x2E, 0x2F, 0x31, 0x85

UDS İstek / Yanıt Formatı

/* İstek:          [SID]     [sub-function / DID / veri]   */
/* Pozitif yanit:  [SID+0x40] [echo / dondurülen veri]    */
/* Negatif yanit:  [0x7F]    [SID]  [NRC kodu]            */

/* Ornek: DiagnosticSessionControl -> ExtendedSession      */
/* Istek:  10 03                                           */
/* Yanitı: 50 03 [P2_hi] [P2_lo] [P2star_hi] [P2star_lo]  */

/* Ornek: ReadDataByIdentifier VIN (DID 0xF190)            */
/* Istek:  22 F1 90                                        */
/* Yanitı: 62 F1 90 57 41 55 5A 5A 5A ...  (17 karakter)  */

/* Ornek: Negatif yanit                                    */
/* 7F 22 31  > 0x7F=NRC, SID=0x22, NRC=0x31 (OutOfRange) */

Yaygın NRC Kodları

NRCAdAçıklama
0x10generalRejectGenel red
0x11serviceNotSupportedBu SID desteklenmiyor
0x13incorrectMessageLengthMesaj uzunluğu veya format hatalı
0x22conditionsNotCorrectKoşullar sağlanmamış (yanlış oturum, güç modu vb.)
0x24requestSequenceErrorHatalı sıralama (SecurityAccess adımı atlandı)
0x31requestOutOfRangeDID veya adres aralık dışı
0x33securityAccessDeniedGüvenlik kilidi açılmamış
0x35invalidKeySecurityAccess key yanlış
0x36exceededNumberOfAttemptsBaşarısız giriş denemesi sayısı aşıldı — ECU geçici kilitli
0x78requestCorrectlyReceived-ResponsePendingECU meşgul, yanıt bekleniyor — TesterPresent göndermeye devam edin

05 UDS İstemci Yazımı (C)

ISO-TP soket üzerine inşa edilmiş UDS istemcisi: oturum açma, DID okuma ve negatif yanıt kodu işleme işlevlerini içeren tam C örneği.

UDS Yardımcı Fonksiyonları

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#define UDS_POS_RESP_OFFSET  0x40
#define UDS_NRC_SID          0x7F

/* UDS isteği gönder ve yanıt al */
/* sock : açık ISO-TP soketi                              */
/* req  : istek baytları (SID dahil)                      */
/* req_len : istek uzunlugu                               */
/* resp     : yanıt tamponu                               */
/* resp_max : tampon boyutu                               */
/* dönüş: alınan bayt sayısı, hata durumunda -1           */
int uds_request(int sock,
                const unsigned char *req,  int req_len,
                unsigned char       *resp, int resp_max)
{
    if (write(sock, req, req_len) < 0) {
        perror("uds_request: write");
        return -1;
    }

    /* NRC 0x78 (ResponsePending) gelirse tekrar bekle */
    int total_wait = 0;
    while (total_wait < 5000) {   /* max 5 saniye */
        int n = (int)read(sock, resp, resp_max);
        if (n < 0) {
            perror("uds_request: read");
            return -1;
        }
        if (n >= 3 && resp[0] == UDS_NRC_SID && resp[2] == 0x78) {
            /* ECU mesgul — 100 ms bekle, tekrar dene */
            usleep(100000);
            total_wait += 100;
            continue;
        }
        return n;
    }
    fprintf(stderr, "uds_request: ResponsePending zaman asimi\n");
    return -1;
}

/* Negatif yanit kontrolu */
int uds_check_response(const unsigned char *resp, int len, unsigned char sid)
{
    if (len < 1) return -1;

    /* Negatif yanit */
    if (resp[0] == UDS_NRC_SID) {
        if (len >= 3) {
            fprintf(stderr, "NRC hatasi: SID=0x%02X NRC=0x%02X\n",
                    resp[1], resp[2]);
        }
        return -1;
    }

    /* Pozitif yanit: beklenen SID + 0x40 olmali */
    if (resp[0] != (unsigned char)(sid + UDS_POS_RESP_OFFSET)) {
        fprintf(stderr, "Beklenmeyen yanit: 0x%02X (beklenen: 0x%02X)\n",
                resp[0], sid + UDS_POS_RESP_OFFSET);
        return -1;
    }

    return 0;
}

DiagnosticSessionControl — Oturum Açma

/* 0x10 DiagnosticSessionControl */
int uds_open_session(int sock, unsigned char session_type)
{
    unsigned char req[2]   = { 0x10, session_type };
    unsigned char resp[64] = {0};

    int n = uds_request(sock, req, sizeof(req), resp, sizeof(resp));
    if (n < 0 || uds_check_response(resp, n, 0x10) < 0)
        return -1;

    /* resp[2..3] = P2_server (ms), resp[4..5] = P2*_server (10ms birimi) */
    unsigned int p2 = (resp[2] << 8) | resp[3];
    printf("Oturum acildi: tip=0x%02X  P2=%u ms\n", session_type, p2);
    return 0;
}

/* Kullanim:
   // Extended session (0x03)
   uds_open_session(sock, 0x03);
   // Programming session (0x02)
   uds_open_session(sock, 0x02);
*/

ReadDataByIdentifier — DID Okuma

/* 0x22 ReadDataByIdentifier */
int uds_read_did(int sock, unsigned short did,
                 unsigned char *out, int out_max)
{
    unsigned char req[3] = {
        0x22,
        (unsigned char)(did >> 8),
        (unsigned char)(did & 0xFF)
    };
    unsigned char resp[4096] = {0};

    int n = uds_request(sock, req, sizeof(req), resp, sizeof(resp));
    if (n < 0 || uds_check_response(resp, n, 0x22) < 0)
        return -1;

    /* resp[0]=0x62, resp[1..2]=DID echo, resp[3..]=veri */
    int data_len = n - 3;
    if (data_len <= 0 || data_len > out_max) return -1;

    memcpy(out, resp + 3, data_len);
    return data_len;
}

/* Kullanim: VIN okuma (DID 0xF190) */
unsigned char vin[18] = {0};
int len = uds_read_did(sock, 0xF190, vin, sizeof(vin) - 1);
if (len == 17) {
    vin[17] = '\0';
    printf("VIN: %s\n", vin);
}

SecurityAccess — Güvenlik Kilidi Açma

/* 0x27 SecurityAccess — seed/key akisi */
int uds_security_access(int sock, unsigned char level,
                        unsigned int secret_key)
{
    unsigned char req[2], resp[64];
    int n;

    /* Adim 1: Seed talep et (accessType = level, tek sayı) */
    req[0] = 0x27;
    req[1] = level;          /* orn: 0x01 */
    n = uds_request(sock, req, 2, resp, sizeof(resp));
    if (n < 0 || uds_check_response(resp, n, 0x27) < 0)
        return -1;

    /* resp[2..5] = 4-baytlık seed */
    unsigned int seed = ((unsigned int)resp[2] << 24) |
                        ((unsigned int)resp[3] << 16) |
                        ((unsigned int)resp[4] <<  8) |
                        (unsigned int)resp[5];
    printf("Seed: 0x%08X\n", seed);

    /* Adim 2: Key hesapla (basit XOR örnegi) */
    unsigned int computed_key = seed ^ secret_key;

    /* Adim 3: Key gönder (accessType = level + 1, cift sayı) */
    unsigned char key_req[6] = {
        0x27, (unsigned char)(level + 1),
        (unsigned char)(computed_key >> 24),
        (unsigned char)(computed_key >> 16),
        (unsigned char)(computed_key >>  8),
        (unsigned char)(computed_key &  0xFF)
    };
    n = uds_request(sock, key_req, sizeof(key_req), resp, sizeof(resp));
    if (n < 0 || uds_check_response(resp, n, 0x27) < 0) {
        fprintf(stderr, "SecurityAccess basarisiz!\n");
        return -1;
    }

    printf("Guvenlik erisimi saglandi.\n");
    return 0;
}

Tam UDS İstemci Kullanım Örneği

int main(void)
{
    int sock = isotp_open("can0", 0x7DF, 0x7E8);
    if (sock < 0) return 1;

    /* Extended session ac */
    if (uds_open_session(sock, 0x03) < 0) goto done;

    /* VIN oku */
    unsigned char vin[18] = {0};
    int len = uds_read_did(sock, 0xF190, vin, 17);
    if (len == 17) printf("VIN: %.17s\n", vin);

    /* Guvenlik erisimi (yazma/kalibrasyon icin) */
    if (uds_security_access(sock, 0x01, 0xDEADBEEF) == 0) {
        /* Kalibrasyon parametresi yaz — ornek DID 0x1234 */
        unsigned char cal_req[] = { 0x2E, 0x12, 0x34, 0x00, 0xFF };
        unsigned char resp[16];
        int n = uds_request(sock, cal_req, sizeof(cal_req),
                            resp, sizeof(resp));
        uds_check_response(resp, n, 0x2E);
    }

done:
    close(sock);
    return 0;
}

06 ECU Programlama Akışı

UDS üzerinden ECU firmware güncellemesi belirli bir servis sırasını gerektirir: oturum geçişi, güvenlik kilidi açma, indirme isteği, veri transferi ve transfer çıkışı.

ECU Programlama Adımları

  1.  DiagnosticSessionControl  -> ProgrammingSession  (0x10 02)
  2.  SecurityAccess            -> Level 2 seed/key    (0x27 03 / 0x27 04)
  3.  ControlDTCSetting         -> off                 (0x85 02)
  4.  CommunicationControl      -> disableRxAndTx      (0x28 03)
  5.  RequestDownload           -> adres + boyut        (0x34)
  6.  TransferData              -> firmware bloklari   (0x36, tekrar)
  7.  RequestTransferExit       -> butunluk dogrula     (0x37)
  8.  RoutineControl            -> CRC dogrulama        (0x31 01 ROUTINE_ID)
  9.  ECUReset                  -> hard reset           (0x11 01)
  10. Yeni firmware aktif
  

RequestDownload (0x34)

/* 0x34 RequestDownload — indirilecek bellek bolgesi bildir */
int uds_request_download(int sock,
                         unsigned int mem_addr,
                         unsigned int mem_size,
                         unsigned int *max_block_out)
{
    /* dataFormatIdentifier: compression=0, encryption=0 */
    /* addressAndLengthFormatIdentifier: 4 bayt adres, 4 bayt uzunluk = 0x44 */
    unsigned char req[10] = {
        0x34,
        0x00,    /* dataFormatIdentifier */
        0x44,    /* addressAndLengthFormatIdentifier */
        /* memoryAddress (4 bayt, big-endian) */
        (unsigned char)(mem_addr >> 24),
        (unsigned char)(mem_addr >> 16),
        (unsigned char)(mem_addr >>  8),
        (unsigned char)(mem_addr &  0xFF),
        /* memorySize (4 bayt, big-endian) */
        (unsigned char)(mem_size >> 24),
        (unsigned char)(mem_size >> 16),
        (unsigned char)(mem_size >>  8),
        /* son bayt asagida */
    };
    /* 10. bayt: mem_size & 0xFF */
    unsigned char req2[11];
    memcpy(req2, req, 10);
    req2[10] = (unsigned char)(mem_size & 0xFF);

    unsigned char resp[16] = {0};
    int n = uds_request(sock, req2, 11, resp, sizeof(resp));
    if (n < 0 || uds_check_response(resp, n, 0x34) < 0)
        return -1;

    /* resp[1]: lengthFormatIdentifier — max blok uzunlugunun kac bayt oldugu */
    int lfi = (resp[1] >> 4) & 0x0F;
    unsigned int max_block = 0;
    for (int i = 0; i < lfi; i++)
        max_block = (max_block << 8) | resp[2 + i];

    *max_block_out = max_block;
    printf("Max blok boyutu: %u bayt\n", max_block);
    return 0;
}

TransferData (0x36) — Blok Döngüsü

/* 0x36 TransferData — firmware bloklarini gonder */
int uds_flash_firmware(int sock, const char *fw_path,
                       unsigned int target_addr)
{
    /* Firmware dosyasini ac */
    FILE *f = fopen(fw_path, "rb");
    if (!f) { perror("fopen"); return -1; }
    fseek(f, 0, SEEK_END);
    long fw_size = ftell(f);
    fseek(f, 0, SEEK_SET);

    unsigned char *fw = malloc(fw_size);
    fread(fw, 1, fw_size, f);
    fclose(f);

    printf("Firmware boyutu: %ld bayt\n", fw_size);

    /* RequestDownload */
    unsigned int max_block = 0;
    if (uds_request_download(sock, target_addr,
                             (unsigned int)fw_size,
                             &max_block) < 0) {
        free(fw);
        return -1;
    }
    max_block -= 2;   /* SID + BSN overhead */

    /* TransferData dongusu */
    unsigned char req[4096 + 2];
    unsigned char resp[16];
    long offset = 0;
    unsigned char bsc = 1;    /* block sequence counter: 1..0xFF dongüsel */

    while (offset < fw_size) {
        long chunk = fw_size - offset;
        if (chunk > max_block) chunk = max_block;

        req[0] = 0x36;
        req[1] = bsc;
        memcpy(req + 2, fw + offset, chunk);

        int n = uds_request(sock, req, (int)(chunk + 2),
                            resp, sizeof(resp));
        if (n < 0 || uds_check_response(resp, n, 0x36) < 0) {
            free(fw);
            return -1;
        }

        offset += chunk;
        bsc = (bsc == 0xFF) ? 0x01 : bsc + 1;
        printf("\rTransfer: %ld/%ld bayt (%ld%%)",
               offset, fw_size, offset * 100 / fw_size);
        fflush(stdout);
    }
    printf("\nTransfer tamamlandi.\n");

    /* RequestTransferExit (0x37) */
    unsigned char exit_req[1] = { 0x37 };
    int n = uds_request(sock, exit_req, 1, resp, sizeof(resp));
    uds_check_response(resp, n, 0x37);

    free(fw);
    return 0;
}

ECU Sıfırlama ve Doğrulama

/* ECUReset sonrasi yeni firmware kontrol */
void uds_ecu_reset(int sock, unsigned char reset_type)
{
    unsigned char req[2]   = { 0x11, reset_type };
    unsigned char resp[8]  = {0};
    uds_request(sock, req, 2, resp, sizeof(resp));
    /* NRC 0x78 veya pozitif yanit beklenir; ECU sifirlanir */
    printf("ECU sifirlanıyor (tip=0x%02X)...\n", reset_type);
}

/* 0x11 0x01 = hardReset */
uds_ecu_reset(sock, 0x01);

07 Python-CAN ve udsoncan Kütüphanesi

python-can + udsoncan kütüphane çifti, hızlı prototipleme, test otomasyonu ve testbench uygulamaları için eksiksiz bir Python UDS istemci yığını sağlar.

Kurulum

# Bağımlılıkları kur
pip3 install python-can can-isotp udsoncan

# Versiyon doğrulama
python3 -c "import udsoncan; print(udsoncan.__version__)"

# can0 arayüzünü hazırla
sudo modprobe can_isotp
sudo ip link set can0 type can bitrate 500000
sudo ip link set can0 up

Tam UDS İstemci Örneği

#!/usr/bin/env python3
"""
udsoncan ile UDS istemci ornegi
SocketCAN (can0) uzerinde ISO-TP ile ECU iletisimi
"""
import can
import isotp
import udsoncan
from udsoncan.connections import PythonIsoTpConnection
from udsoncan.services   import DiagnosticSessionControl as DSC
from udsoncan.services   import ReadDTCInformation

# --- Baglanti kurulumu ----------------------------------------------
bus = can.Bus(interface="socketcan", channel="can0", bitrate=500000)
addr = isotp.Address(
    isotp.AddressingMode.Normal_11bits,
    txid=0x7DF,   # OBD-II broadcast tester ID
    rxid=0x7E8,   # ECU yanit ID
)
stack = isotp.CanStack(
    bus=bus,
    address=addr,
    params=isotp.params.Params(blocksize=0, squash_stmin_requirement=False),
)

# --- UDS istemci konfigürasyonu -------------------------------------
config = udsoncan.configs.default_client_config.copy()
config["data_identifiers"] = {
    0xF190: udsoncan.AsciiCodec(17),    # VIN — 17 karakter ASCII
    0xF194: udsoncan.AsciiCodec(10),    # ECU donanim numarasi
    0xF195: udsoncan.AsciiCodec(10),    # ECU yazilim numarasi
}

# --- Kullanim --------------------------------------------------------
with PythonIsoTpConnection(stack) as conn:
    with udsoncan.Client(conn, config=config) as client:

        # 1) VIN oku (Default Session)
        resp = client.read_data_by_identifier(0xF190)
        vin = resp.service_data.values[0xF190]
        print(f"VIN: {vin}")

        # 2) Extended Session'a gec
        client.change_session(DSC.Session.extendedDiagnosticSession)

        # 3) Aktif DTC'leri oku
        resp = client.get_dtc_by_status_mask(0xFF)
        dtcs = resp.service_data.dtcs
        if dtcs:
            for dtc in dtcs:
                print(f"  DTC: {dtc.id:06X}  Durum: {dtc.status.byte}")
        else:
            print("Aktif DTC yok.")

        # 4) Default Session'a geri don
        client.change_session(DSC.Session.defaultSession)

SecurityAccess ile Kalibrasyon Yazma

#!/usr/bin/env python3
import udsoncan
from udsoncan.connections import PythonIsoTpConnection

SECRET = 0xDEADBEEF

def compute_key(seed_bytes: bytes) -> bytes:
    seed = int.from_bytes(seed_bytes, "big")
    key  = seed ^ SECRET
    return key.to_bytes(4, "big")

with PythonIsoTpConnection(stack) as conn:
    with udsoncan.Client(conn) as client:

        # Extended session
        client.change_session(
            udsoncan.services.DiagnosticSessionControl.Session
            .extendedDiagnosticSession)

        # Seed talep et
        resp = client.request_seed(access_type=0x01)
        seed = resp.service_data.seed
        print("Seed:", seed.hex().upper())

        # Key hesapla ve gönder
        key = compute_key(seed)
        client.send_key(access_type=0x02, security_key=key)
        print("Guvenlik erisimi saglandi!")

        # Kalibrasyon DID yaz (ornek: 0x1234 = 0x00FF)
        client.write_data_by_identifier(
            0x1234,
            udsoncan.RawCodec(2).decode(bytes([0x00, 0xFF]))
        )
        print("Kalibrasyon yazildi.")

TesterPresent ile Oturumu Canlı Tutma

import threading, time

def tester_present_loop(client, interval=2.0):
    """Her interval saniyede bir TesterPresent gonder"""
    while getattr(tester_present_loop, "running", True):
        try:
            client.tester_present()
        except Exception as exc:
            print("TesterPresent hatasi:", exc)
            break
        time.sleep(interval)

# Arka planda calistir
tester_present_loop.running = True
tp = threading.Thread(target=tester_present_loop, args=(client,), daemon=True)
tp.start()

# ... uzun suren islemler ...

tester_present_loop.running = False

08 Hata Ayıklama Araçları

CAN trafik izleme araçları, ISO-TP dump yardımcıları ve Wireshark CAN dissector ile UDS iletişim sorunlarını tespit etmek mümkündür.

candump ile Trafik İzleme

# Tüm CAN çerçevelerini izle
candump can0

# ID filtreli izleme — sadece 0x7DF ve 0x7E8
candump can0 7DF:7FF 7E8:7FF

# Zaman damgalı ve ASCII dump
candump -t a -a can0

# Log dosyasına kaydet
candump -L can0 > /tmp/can_session.log

# Log dosyasını yeniden oynat
canplayer -I /tmp/can_session.log can0

isotpsend / isotprecv ile Hızlı Test

# isotpsend: tek payload gonder (ISO-TP cercevelemesi otomatik)
isotpsend -s 7DF -d 7E8 can0 <<< "22 F1 90"

# isotprecv: yanit bekle (arka planda calıstır)
isotprecv -s 7E8 -d 7DF can0 &

# isotpdump: ISO-TP trafigi birlestirilerek goster
isotpdump -s 7DF -d 7E8 can0

# Ornek isotpdump ciktisi:
# 0.000123 can0 TX [3] 22 F1 90
# 0.003456 can0 RX [20] 62 F1 90 57 41 55 5A 5A 5A ...

# Zaman damgali cikti
isotpdump -t -s 7DF -d 7E8 can0

Wireshark ile Analiz

# candump log'unu pcap'e donustur
candump -L can0 > can_session.log
# cantools ile pcap'e cevir
python3 -m cantools convert can_session.log capture.pcap

# Wireshark CLI ile ISO-TP dissector
tshark -r capture.pcap \
       -Y "can.id == 0x7df || can.id == 0x7e8" \
       -T fields \
       -e frame.time_relative \
       -e can.id \
       -e can.data

udsoncan ile Debug Loglaması

import logging

# udsoncan ve isotp debug loglarini etkinlestir
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("udsoncan").setLevel(logging.DEBUG)
logging.getLogger("isotp").setLevel(logging.DEBUG)

# Ornek cikti:
# DEBUG udsoncan: Sending 0x10 0x03
# DEBUG isotp:    Sending SF id=0x7DF data=10 03
# DEBUG isotp:    Received SF id=0x7E8 data=50 03 00 19 01 F4
# DEBUG udsoncan: Received positive response 0x50 0x03

Sık Karşılaşılan Sorunlar ve Çözümleri

SorunSemptomÇözüm
Yanıt yokTX görünüyor, RX yokRX ID'yi kontrol et; ECU aktif mi? Oturum modu doğru mu?
NRC 0x22conditionsNotCorrectÖnce ExtendedSession veya ProgrammingSession'a geç
NRC 0x33securityAccessDenied0x27 RequestSeed → key hesapla → SendKey akışını tamamla
NRC 0x35invalidKeySeed/key algoritması hatalı; ECU dokümanını kontrol et
NRC 0x36exceededNumberOfAttemptsECU geçici kilitli; OEM belgelenen bekleme süresini uygula
NRC 0x78 tekrar gelmiyorResponsePending sona erdiPoling süresini artır; TesterPresent göndermeye devam et
ISO-TP Flow Control gelmiyorFF gönderildi, FC yokcan_isotp modülü yüklü mü? Soket bind adresleri doğru mu?
TransferData sekans hatasıNRC 0x73block_sequence_counter 0x01'den başlamalı; 0xFF sonrası 0x01'e döner
Padding hatasıBind başarısız veya garip yanıtlarECU TX_PADDING bekliyorsa CAN_ISOTP_TX_PADDING bayrağını set et

SPN/DID Referans Kaynakları

ISO 14229-1Temel UDS protokol belgesi — servis tanımları ve NRC kodları. Resmi ISO dokümanı gereklidir.
python-can docshttps://python-can.readthedocs.io — socketcan arayüzü konfigürasyonu ve bus sınıfı kullanımı.
udsoncan docshttps://udsoncan.readthedocs.io — tüm UDS servis sınıfları, codec yapısı ve örnek kullanım.
can-isotp kernel docslinux/Documentation/networking/can_isotp.rst — kernel ISO-TP soket API'si tam referansı.
OBD-II PID tablosuSAE J1979 — Mode 01 PID listesi; motor, emisyon ve sensör veri tanımları.